Comparison of Methods

Here we want to determine the precision and recall of our methods using the synthetic datasets.

A. Synthetic data with low and high variability

# install.packages("MASS")
# install.packages("ggplot2")
# install.packages("dplyr")
# install.packages("tidyr")
# install.packages("sf") # New dependency for geometric operations (overlap calculation)

library(MASS) # For mvrnorm
library(ggplot2)
library(dplyr)
library(tidyr)
library(sf) # For spatial data operations to calculate ellipse overlap

# --- 1. Define Dataset Parameters ---
num_genes <- 300 # N = 300 genes
samples_per_condition <- 50 # M = 50 samples per condition
num_conditions <- 2 # Conditions Healthy and Diseased
num_modules <- 10 # Number of predefined gene modules

# Set a seed for reproducibility
set.seed(42)

# --- 2. Assign Known Module Membership to Genes ---
# Each gene is assigned to one of the 'num_modules'.
# Genes are divided equally among modules.
genes_per_module <- num_genes %/% num_modules # Integer division
gene_ids <- paste0("Gene_", 1:num_genes)

# Create module assignments (e.g., Gene_001 to Gene_030 are Module_1, etc.)
module_nums <- ((0:(num_genes-1)) %/% genes_per_module) + 1
gene_module_membership <- data.frame(
    Gene = gene_ids,
    Module = paste0("Module_", module_nums)
)

cat("--- Gene Module Membership (first 10 genes) ---\n")
--- Gene Module Membership (first 10 genes) ---
print(head(gene_module_membership, 10))
cat(paste0(rep("-", 50), collapse = ""), "\n\n")
-------------------------------------------------- 
# --- 3. Define Parameters for Mean Expression Profiles and Overlap on LOG SCALE ---
# These parameters now refer to the log-transformed gene expression values.
log_baseline_mean <- log(10.0) # Base mean expression level on log scale (e.g., log(10) = 2.3)

# Magnitude of the module-specific effect on the LOG scale.
# A smaller value here means less separation on the log scale, leading to more overlap
# in the PCA space of the final, exponentiated data.
# log_module_effect_magnitude <- 0.9 # This value is high, expecting low overlap.
log_module_effect_magnitude <- 0.1 # High overlap

# New: Parameter for nonlinear (cubic) correlation strength
# This controls the magnitude of the shared cubic effect within modules.
# A small value is recommended as cubic terms grow rapidly.
nonlinear_correlation_strength <- 0.1 # Adjust this value to control the cubic effect's magnitude

# --- 4. Construct Covariance Matrix for Within-Module Co-expression on LOG SCALE ---
# This matrix will define the linear correlation structure between genes on the log scale.
# Genes within the same module will be linearly correlated; genes in different modules will not.
# The cubic term will add an additional, non-linear correlation on top of this.

# Controls the base variance of each gene on the log scale
log_gene_variance <- 0.3 # Reduced to avoid excessively wide tails after exponentiation

# Controls the linear correlation strength within a module (0 to 1) on the log scale
within_module_correlation <- 0.5

# Initialize a diagonal covariance matrix (genes are initially independent)
covariance_matrix <- diag(num_genes) * log_gene_variance
colnames(covariance_matrix) <- gene_ids
rownames(covariance_matrix) <- gene_ids

# Populate the covariance matrix for within-module linear co-expression
for (i in 1:num_genes) {
    for (j in i:num_genes) { # Iterate through upper triangle (matrix is symmetric)
        if (i == j) {
            # Diagonal elements are variances (already set)
            next
        }
        
        gene1_module <- gene_module_membership$Module[gene_module_membership$Gene == gene_ids[i]]
        gene2_module <- gene_module_membership$Module[gene_module_membership$Gene == gene_ids[j]]
        
        if (gene1_module == gene2_module) {
            # If genes are in the same module, set their covariance based on linear correlation
            # Cov(X,Y) = Corr(X,Y) * SD(X) * SD(Y)
            # Assuming SD(X) = SD(Y) = sqrt(log_gene_variance)
            covariance_value <- within_module_correlation * log_gene_variance
            covariance_matrix[i, j] <- covariance_value
            covariance_matrix[j, i] <- covariance_value # Symmetric
        }
    }
}

cat("--- Sample of Covariance Matrix (first 5x5 block, on log scale) ---\n")
--- Sample of Covariance Matrix (first 5x5 block, on log scale) ---
print(round(covariance_matrix[1:5, 1:5], 2))
       Gene_1 Gene_2 Gene_3 Gene_4 Gene_5
Gene_1   0.30   0.15   0.15   0.15   0.15
Gene_2   0.15   0.30   0.15   0.15   0.15
Gene_3   0.15   0.15   0.30   0.15   0.15
Gene_4   0.15   0.15   0.15   0.30   0.15
Gene_5   0.15   0.15   0.15   0.15   0.30
cat(paste0(rep("-", 50), collapse = ""), "\n\n")
-------------------------------------------------- 
# --- 5. Define Mean Expression Profiles for Healthy and Diseased (Module-aware, on LOG SCALE) ---
mean_profile_Healthy_log <- rep(log_baseline_mean, num_genes)
mean_profile_Diseased_log <- rep(log_baseline_mean, num_genes)

for (i in 1:num_genes) {
    gene_id <- gene_ids[i]
    module_id_str <- gene_module_membership$Module[gene_module_membership$Gene == gene_id]
    module_num <- as.numeric(strsplit(module_id_str, "_")[[1]][2])

    # Differential expression strategy:
    # Modules 1 to (num_modules/2) will have higher expression in Condition Healthy
    # Modules (num_modules/2 + 1) to num_modules will have higher expression in Condition Diseased
    if (module_num <= num_modules / 2) {
        mean_profile_Healthy_log[i] <- mean_profile_Healthy_log[i] + log_module_effect_magnitude
    } else {
        mean_profile_Diseased_log[i] <- mean_profile_Diseased_log[i] + log_module_effect_magnitude
    }
}

cat("--- Sample of Mean Expression Profiles (first 10 genes, on log scale) ---\n")
--- Sample of Mean Expression Profiles (first 10 genes, on log scale) ---
print(data.frame(Gene = gene_ids[1:10], Mean_Healthy_log = mean_profile_Healthy_log[1:10], Mean_Diseased_log = mean_profile_Diseased_log[1:10]))
cat(paste0(rep("-", 50), collapse = ""), "\n\n")
-------------------------------------------------- 
# --- 6. Generate Synthetic Gene Expression Data (from Log-Normal Distribution with Cubic Term) ---

# Generate module-specific latent variables for cubic effect
# These will be shared by all genes within a module for a given sample, and are independent of mvrnorm noise
module_latent_vars_healthy <- matrix(rnorm(num_modules * samples_per_condition),
                                     nrow = num_modules,
                                     ncol = samples_per_condition)
module_latent_vars_diseased <- matrix(rnorm(num_modules * samples_per_condition),
                                      nrow = num_modules,
                                      ncol = samples_per_condition)


# Generate base data for Healthy samples (linearly correlated on log-scale)
expression_Healthy_log_base <- t(MASS::mvrnorm(n = samples_per_condition,
                                               mu = mean_profile_Healthy_log,
                                               Sigma = covariance_matrix))

# Add the cubic term to introduce non-linear correlation within modules
expression_Healthy_log <- expression_Healthy_log_base
for (s in 1:samples_per_condition) {
    for (g_idx in 1:num_genes) {
        module_num <- as.numeric(strsplit(gene_module_membership$Module[g_idx], "_")[[1]][2])
        # Add the scaled cubic term
        cubic_term <- nonlinear_correlation_strength * (module_latent_vars_healthy[module_num, s])^3
        expression_Healthy_log[g_idx, s] <- expression_Healthy_log[g_idx, s] + cubic_term
    }
}
expression_Healthy <- exp(expression_Healthy_log) # Convert from log to linear scale
colnames(expression_Healthy) <- paste0("Healthy_Sample_", 1:samples_per_condition)
rownames(expression_Healthy) <- paste0("Gene_", 1:num_genes)


# Generate base data for Diseased samples (linearly correlated on log-scale)
expression_Diseased_log_base <- t(MASS::mvrnorm(n = samples_per_condition,
                                                mu = mean_profile_Diseased_log,
                                                Sigma = covariance_matrix))

# Add the cubic term to introduce non-linear correlation within modules
expression_Diseased_log <- expression_Diseased_log_base
for (s in 1:samples_per_condition) {
    for (g_idx in 1:num_genes) {
        module_num <- as.numeric(strsplit(gene_module_membership$Module[g_idx], "_")[[1]][2])
        # Add the scaled cubic term
        cubic_term <- nonlinear_correlation_strength * (module_latent_vars_diseased[module_num, s])^3
        expression_Diseased_log[g_idx, s] <- expression_Diseased_log[g_idx, s] + cubic_term
    }
}
expression_Diseased <- exp(expression_Diseased_log) # Convert from log to linear scale
colnames(expression_Diseased) <- paste0("Diseased_Sample_", 1:samples_per_condition)
rownames(expression_Diseased) <- paste0("Gene_", 1:num_genes)

# Ensure all expression values are non-negative (clip at a small value to avoid exact zero)
expression_Healthy[expression_Healthy < 0.01] <- 0.01
expression_Diseased[expression_Diseased < 0.01] <- 0.01


# Combine into a single gene expression matrix
synthetic_expression_data <- cbind(expression_Healthy, expression_Diseased)


# --- 7. Create Sample Metadata (Phenotype/Condition Labels) ---
sample_metadata <- data.frame(
  Sample_ID = colnames(synthetic_expression_data),
  Condition = c(rep("Healthy", samples_per_condition), rep("Diseased", samples_per_condition))
)
rownames(sample_metadata) <- sample_metadata$Sample_ID

print("--- Synthetic Dataset Created ---")
[1] "--- Synthetic Dataset Created ---"
print(paste("Dimensions of expression data (genes x samples):",
            dim(synthetic_expression_data)[1], "x", dim(synthetic_expression_data)[2]))
[1] "Dimensions of expression data (genes x samples): 300 x 100"
print("First 5 rows and 5 columns of expression data (linear scale):")
[1] "First 5 rows and 5 columns of expression data (linear scale):"
print(head(synthetic_expression_data[, 1:5]))
       Healthy_Sample_1 Healthy_Sample_2 Healthy_Sample_3 Healthy_Sample_4 Healthy_Sample_5
Gene_1         39.98632        14.615295         7.536453         13.19507         9.511583
Gene_2         22.55743        10.669340        13.297870         10.86345         4.735773
Gene_3         12.54379        18.601410        11.303937         14.17025         5.552115
Gene_4         14.69860        14.041051        12.808648         27.63371         9.389464
Gene_5         17.96696         8.539735         5.759028          7.69522         7.278582
Gene_6         14.61363        16.171502         6.560317         14.67489         7.858207
print("First 5 rows of sample metadata:")
[1] "First 5 rows of sample metadata:"
print(head(sample_metadata))
cat(paste0(rep("-", 50), collapse = ""), "\n\n")
-------------------------------------------------- 
# --- Save synthetic_expression_data, sample_metadata, and gene_module_membership ---
# Construct filenames to include module_effect_magnitude
expression_filename <- paste0("synthetic_expression_data_effect_", log_module_effect_magnitude, ".csv")
metadata_filename <- paste0("sample_metadata_effect_", log_module_effect_magnitude, ".csv")
module_membership_filename <- paste0("gene_module_membership_effect_", log_module_effect_magnitude, ".csv")


write.csv(synthetic_expression_data, expression_filename, row.names = TRUE)
cat(sprintf("Saved %s\n", expression_filename))
Saved synthetic_expression_data_effect_0.1.csv
write.csv(sample_metadata, metadata_filename, row.names = FALSE)
cat(sprintf("Saved %s\n", metadata_filename))
Saved sample_metadata_effect_0.1.csv
write.csv(gene_module_membership, module_membership_filename, row.names = FALSE)
cat(sprintf("Saved %s\n\n", module_membership_filename))
Saved gene_module_membership_effect_0.1.csv
# --- 8. Verify Overlap with PCA Plot ---

# Transpose the expression data for PCA (PCA expects samples as rows, genes as columns)
pca_input_data <- t(synthetic_expression_data)

# Perform PCA
pca_results <- prcomp(pca_input_data, center = TRUE, scale. = TRUE) # Scale is usually good for gene expression PCA

# Extract PCA scores (coordinates of samples in PCA space)
pca_scores <- as.data.frame(pca_results$x)

# Add condition labels to the PCA scores
pca_scores$Condition <- sample_metadata[rownames(pca_scores), "Condition"]

# Calculate explained variance for PC1 and PC2
pca_summary <- summary(pca_results)
pc1_var_explained <- round(pca_summary$importance[2, 1] * 100, 2)
pc2_var_explained <- round(pca_summary$importance[2, 2] * 100, 2)

# --- Function to generate closed ellipse points for overlap calculation ---
ellipse_points <- function(df, level = 0.95, n = 100) {
  # For confidence ellipses, the scaling factor from stats::ellipse is chisq(level)/2
  # For normal distribution, it's sqrt(qchisq(level, df = 2))
  # The stat_ellipse uses a t-distribution by default, or normal if type="norm"
  # Here we use the standard deviation for scaling to match a specific confidence region
  
  # Calculate 95% confidence region scaling factor from a chi-squared distribution with 2 degrees of freedom
  # (since we are in 2D for PC1 and PC2)
  scale_factor <- sqrt(qchisq(level, df = 2))
  
  cov_mat <- cov(df[, c("PC1", "PC2")])
  mean_vals <- colMeans(df[, c("PC1", "PC2")])
  
  angles <- seq(0, 2 * pi, length.out = n)
  
  # Scale by the square root of the eigenvalues and rotate by eigenvectors (cholesky decomposition)
  ellipse_coords <- t(chol(cov_mat)) %*% rbind(cos(angles), sin(angles))
  
  coords <- data.frame(
    PC1 = mean_vals[1] + scale_factor * ellipse_coords[1,],
    PC2 = mean_vals[2] + scale_factor * ellipse_coords[2,]
  )
  
  # Close the polygon by adding the first point at the end
  coords <- rbind(coords, coords[1,])
  return(coords)
}

# --- Compute Overlap of Ellipses ---
# Use the pca_scores as the pca_df mentioned in the user's snippet
pca_df_for_overlap <- pca_scores

# Generate closed ellipse points for each condition
ellipse_healthy <- ellipse_points(pca_df_for_overlap %>% filter(Condition == "Healthy"))
ellipse_diseased <- ellipse_points(pca_df_for_overlap %>% filter(Condition == "Diseased"))

# Convert to sf polygons
# Need to set a CRS, 4326 is WGS84, common for geographic coords, but can be arbitrary for abstract space
poly_healthy <- st_polygon(list(as.matrix(ellipse_healthy)))
poly_diseased <- st_polygon(list(as.matrix(ellipse_diseased)))

# Set CRS to ensure st_intersection works (can be any valid CRS for arbitrary coordinates)
poly_healthy_sfc <- st_sfc(poly_healthy, crs = 4326)
poly_diseased_sfc <- st_sfc(poly_diseased, crs = 4326)

# Compute overlap area
intersection_geometry <- st_intersection(poly_healthy_sfc, poly_diseased_sfc)
# Convert sf::units to numeric before operations
intersect_area <- as.numeric(st_area(intersection_geometry))

# Calculate total area for normalization
area_healthy <- as.numeric(st_area(poly_healthy_sfc))
area_diseased <- as.numeric(st_area(poly_diseased_sfc))

# "Total area" for overlap percentage usually means the area of the smaller ellipse
# or the sum of both areas. User specified min(area_healthy, area_diseased).
total_area_for_overlap_calc <- min(area_healthy, area_diseased)


overlap_value <- if (isTRUE(!is.na(intersect_area) && total_area_for_overlap_calc > 0)) {(intersect_area / total_area_for_overlap_calc) * 100
} else { 0 }

# Ensure overlap_value is not NaN if there's no intersection
if (is.nan(overlap_value)) {
  overlap_value <- 0
}


# --- Plot PCA1 vs PCA2 with customized theme and overlap annotation ---
cat("\n--- Generating PCA Plot ---\n")

--- Generating PCA Plot ---
pca_plot <- ggplot(pca_scores, aes(x = PC1, y = PC2, color = Condition, fill = Condition)) +
  geom_point(size = 3, alpha = 0.8) + # Scatter points # Create and fill ellipses
  stat_ellipse(geom = "polygon", alpha = 0.2, linetype = "solid", level = 0.95) +
  labs(
    title = paste0("Synthetic Gene Expression Data (Effect: ", log_module_effect_magnitude, ")"),
    x = paste0("PC1 (", pc1_var_explained, "%)"),
    y = paste0("PC2 (", pc2_var_explained, "%)")
  ) +
  theme_minimal() +
  theme(
    panel.grid = element_blank(),# Remove gridlines
    axis.line = element_line(color = "black"), # Add axis lines
    axis.title = element_text(size = 14, face = "bold"), # Increase font size of axis labels
    axis.text = element_text(size = 12), # Increase tick label size
    plot.title = element_text(hjust = 0.5), # Center title
    legend.position = "bottom"
    )

# Get plot limits after defining the plot object 'pca_plot'
# This is crucial for correctly positioning the annotation
plot_limits <- ggplot_build(pca_plot)$layout$panel_params[[1]]
xmin <- plot_limits$x.range[1]
ymin <- plot_limits$y.range[1]

# Add annotation at lower-left
final_pca_plot <- pca_plot +
  annotate("text", x = xmin + 0.05 * (plot_limits$x.range[2] - xmin),
           y = ymin + 0.05 * (plot_limits$y.range[2] - ymin),
           label = sprintf("Overlap = %.2f%%", overlap_value),
           size = 5, hjust = 0, vjust = 0, color = "black")

print(final_pca_plot)

# --- Save PCA Plot ---
pca_plot_filename <- paste0("pca_plot_effect_", log_module_effect_magnitude, ".png")
ggsave(pca_plot_filename, plot = final_pca_plot, width = 8, height = 6, dpi = 300, bg = "white")

cat(sprintf("Saved %s\n\n", pca_plot_filename))
Saved pca_plot_effect_0.1.png
# To see how much variance is explained by the first few PCs:
print("Variance explained by PCs:")
[1] "Variance explained by PCs:"
print(summary(pca_results))
Importance of components:
                          PC1    PC2    PC3     PC4     PC5     PC6     PC7     PC8     PC9   PC10    PC11    PC12
Standard deviation     5.9975 5.6944 5.5625 5.37626 5.13735 4.68516 4.45587 4.10204 3.83832 3.5583 1.61729 1.54558
Proportion of Variance 0.1199 0.1081 0.1031 0.09635 0.08797 0.07317 0.06618 0.05609 0.04911 0.0422 0.00872 0.00796
Cumulative Proportion  0.1199 0.2280 0.3311 0.42748 0.51545 0.58862 0.65480 0.71089 0.76000 0.8022 0.81092 0.81888
                          PC13    PC14    PC15    PC16    PC17   PC18    PC19    PC20    PC21    PC22    PC23    PC24
Standard deviation     1.41677 1.39036 1.37102 1.31318 1.31255 1.2614 1.24605 1.20463 1.15412 1.13821 1.12768 1.11778
Proportion of Variance 0.00669 0.00644 0.00627 0.00575 0.00574 0.0053 0.00518 0.00484 0.00444 0.00432 0.00424 0.00416
Cumulative Proportion  0.82558 0.83202 0.83828 0.84403 0.84978 0.8551 0.86025 0.86509 0.86953 0.87385 0.87809 0.88225
                          PC25    PC26    PC27    PC28    PC29   PC30    PC31    PC32    PC33    PC34    PC35    PC36
Standard deviation     1.09930 1.07351 1.05472 1.03486 1.01498 1.0101 0.99367 0.97079 0.96641 0.94659 0.93046 0.92908
Proportion of Variance 0.00403 0.00384 0.00371 0.00357 0.00343 0.0034 0.00329 0.00314 0.00311 0.00299 0.00289 0.00288
Cumulative Proportion  0.88628 0.89012 0.89383 0.89740 0.90084 0.9042 0.90753 0.91067 0.91378 0.91677 0.91965 0.92253
                          PC37    PC38    PC39    PC40    PC41    PC42    PC43    PC44    PC45    PC46    PC47
Standard deviation     0.91057 0.90483 0.89520 0.88891 0.87789 0.86358 0.83940 0.82847 0.81031 0.79885 0.79708
Proportion of Variance 0.00276 0.00273 0.00267 0.00263 0.00257 0.00249 0.00235 0.00229 0.00219 0.00213 0.00212
Cumulative Proportion  0.92530 0.92802 0.93070 0.93333 0.93590 0.93838 0.94073 0.94302 0.94521 0.94734 0.94946
                          PC48    PC49    PC50    PC51    PC52    PC53    PC54    PC55    PC56    PC57   PC58    PC59
Standard deviation     0.78352 0.77715 0.75752 0.74722 0.73721 0.72275 0.71252 0.70847 0.69643 0.67728 0.6699 0.65457
Proportion of Variance 0.00205 0.00201 0.00191 0.00186 0.00181 0.00174 0.00169 0.00167 0.00162 0.00153 0.0015 0.00143
Cumulative Proportion  0.95150 0.95351 0.95543 0.95729 0.95910 0.96084 0.96253 0.96421 0.96582 0.96735 0.9688 0.97028
                          PC60    PC61   PC62    PC63    PC64    PC65    PC66    PC67    PC68    PC69   PC70    PC71
Standard deviation     0.64663 0.63986 0.6256 0.61647 0.61090 0.60308 0.58699 0.58199 0.56844 0.55709 0.5478 0.53352
Proportion of Variance 0.00139 0.00136 0.0013 0.00127 0.00124 0.00121 0.00115 0.00113 0.00108 0.00103 0.0010 0.00095
Cumulative Proportion  0.97167 0.97303 0.9743 0.97561 0.97685 0.97806 0.97921 0.98034 0.98142 0.98245 0.9835 0.98440
                          PC72    PC73    PC74    PC75    PC76    PC77    PC78    PC79    PC80    PC81    PC82
Standard deviation     0.52746 0.52144 0.50392 0.50171 0.49259 0.48332 0.47285 0.46576 0.46154 0.45619 0.43384
Proportion of Variance 0.00093 0.00091 0.00085 0.00084 0.00081 0.00078 0.00075 0.00072 0.00071 0.00069 0.00063
Cumulative Proportion  0.98533 0.98623 0.98708 0.98792 0.98873 0.98951 0.99025 0.99098 0.99169 0.99238 0.99301
                          PC83   PC84    PC85    PC86    PC87    PC88    PC89    PC90    PC91    PC92    PC93    PC94
Standard deviation     0.43317 0.4242 0.41429 0.40223 0.39071 0.37418 0.36636 0.35735 0.34233 0.32954 0.32829 0.31500
Proportion of Variance 0.00063 0.0006 0.00057 0.00054 0.00051 0.00047 0.00045 0.00043 0.00039 0.00036 0.00036 0.00033
Cumulative Proportion  0.99363 0.9942 0.99480 0.99534 0.99585 0.99632 0.99677 0.99719 0.99758 0.99795 0.99830 0.99864
                          PC95    PC96    PC97    PC98    PC99     PC100
Standard deviation     0.30976 0.29349 0.28633 0.27680 0.26230 2.663e-15
Proportion of Variance 0.00032 0.00029 0.00027 0.00026 0.00023 0.000e+00
Cumulative Proportion  0.99895 0.99924 0.99952 0.99977 1.00000 1.000e+00
cat(paste0(rep("-", 50), collapse = ""), "\n\n")
-------------------------------------------------- 
# --- 9. Compute Overlap Percentage (Estimated using Logistic Regression) ---
# This is a separate estimation of overlap from the geometric one.

cat("\n--- Computing Overlap Percentage (Estimated via Logistic Regression) ---\n")

--- Computing Overlap Percentage (Estimated via Logistic Regression) ---
# Ensure 'Condition' is a factor for classification
pca_scores$Condition <- factor(pca_scores$Condition)

# Train a logistic regression model
logistic_model <- glm(Condition ~ PC1 + PC2, data = pca_scores, family = "binomial")

# Predict probabilities
probabilities <- predict(logistic_model, type = "response")

# Convert probabilities to class predictions
# The levels should match 'Healthy' and 'Diseased'
predicted_classes_lr <- factor(ifelse(probabilities > 0.5, "Diseased", "Healthy"), levels = levels(pca_scores$Condition))

# Calculate misclassification rate
misclassification_rate_lr <- mean(predicted_classes_lr != pca_scores$Condition)
overlap_percentage_lr <- round(misclassification_rate_lr * 100, 2)

cat(sprintf("Estimated Overlap Percentage (Logistic Regression misclassification): %.2f%%\n", overlap_percentage_lr))
Estimated Overlap Percentage (Logistic Regression misclassification): 56.00%
cat(paste0(rep("=", 50), collapse = ""), "\n")
================================================== 
NA
NA
NA

B.0 Data preprocessing

#renv::install('tibble')
library(tibble)
library(Biobase)
library(annotate)

phenData = AnnotatedDataFrame(
  read.csv(paste0("sample_metadata_effect_",variability,".csv"), header=T, row.names = 1)
)
eSet = new("ExpressionSet",
           exprs= as.matrix(synthetic_expression_data),
           phenoData = phenData
           )
eSet
ExpressionSet (storageMode: lockedEnvironment)
assayData: 300 features, 100 samples 
  element names: exprs 
protocolData: none
phenoData
  sampleNames: Healthy_Sample_1 Healthy_Sample_2 ... Diseased_Sample_50 (100 total)
  varLabels: Condition
  varMetadata: labelDescription
featureData: none
experimentData: use 'experimentData(object)'
Annotation:  
summary(exprs(eSet))[,1:5]
 Healthy_Sample_1  Healthy_Sample_2   Healthy_Sample_3 Healthy_Sample_4 Healthy_Sample_5
 Min.   :  1.997   Min.   :  0.5559   Min.   : 1.808   Min.   : 1.150   Min.   : 1.583  
 1st Qu.: 11.168   1st Qu.:  6.9661   1st Qu.: 6.641   1st Qu.: 6.635   1st Qu.: 8.984  
 Median : 20.813   Median : 18.0270   Median :11.539   Median :14.324   Median :16.189  
 Mean   : 25.438   Mean   : 28.3225   Mean   :15.859   Mean   :18.243   Mean   :17.348  
 3rd Qu.: 36.618   3rd Qu.: 31.5246   3rd Qu.:21.709   3rd Qu.:26.133   3rd Qu.:23.273  
 Max.   :121.242   Max.   :233.2341   Max.   :83.059   Max.   :65.514   Max.   :96.859  
#renv::install('limma')
library(limma)

normData = normalizeBetweenArrays(exprs(eSet))
eSet.norm = new("ExpressionSet",
           exprs= as.matrix(normData),
           phenoData = phenData)
boxplot(exprs(eSet.norm)[,1:10])

summary(exprs(eSet.norm))[,1:5]
 Healthy_Sample_1  Healthy_Sample_2  Healthy_Sample_3  Healthy_Sample_4  Healthy_Sample_5 
 Min.   :  2.559   Min.   :  2.559   Min.   :  2.559   Min.   :  2.559   Min.   :  2.559  
 1st Qu.:  9.478   1st Qu.:  9.478   1st Qu.:  9.478   1st Qu.:  9.478   1st Qu.:  9.478  
 Median : 16.138   Median : 16.138   Median : 16.138   Median : 16.138   Median : 16.138  
 Mean   : 24.848   Mean   : 24.848   Mean   : 24.848   Mean   : 24.848   Mean   : 24.848  
 3rd Qu.: 27.477   3rd Qu.: 27.477   3rd Qu.: 27.477   3rd Qu.: 27.477   3rd Qu.: 27.477  
 Max.   :194.789   Max.   :194.789   Max.   :194.789   Max.   :194.789   Max.   :194.789  
library(WGCNA)
dat.matrix = exprs(eSet.norm)
dat.expr = as.data.frame(t(dat.matrix))

gsg = goodSamplesGenes(dat.expr, verbose = 3)
 Flagging genes and samples with too many missing values...
  ..step 1
if(sum(!gsg$goodSamples)>0) {
  summary(exprs(eSet.norm.combat))[,!gsg$goodSamples]
}
sprintf("There are %d bad samples in the dataset.", sum(!gsg$goodSamples))
[1] "There are 0 bad samples in the dataset."
if(sum(!gsg$goodGenes)) {
  bad.genes.list = colnames(dat.expr)[!gsg$goodGenes]
}
sprintf('There are %d bad genes in the dataset.', sum(!gsg$goodGenes))
[1] "There are 0 bad genes in the dataset."
sampleTree = hclust(dist(dat.expr), method = "average")
plot(sampleTree, 
     main = "Tree cluster for outlier detection", sub="", xlab="", 
     cex.lab = 1.5, cex.axis = 1.5, cex.main = 2,
     )

h=25
clust = cutreeStatic(sampleTree, cutHeight = h, minSize = 10)
table(clust)
clust
  0 
100 

B. WGCNA Method

library(WGCNA)
options(stringsAsFactors = FALSE)
disableWGCNAThreads()
ggset = as.data.frame(t(exprs(eSet.norm)))
head(ggset)
softPower = 1
adj = adjacency(ggset, power = softPower)
dim(adj)
[1] 300 300
TOM = TOMsimilarity(adj);
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
dissTOM = 1-TOM
dim(TOM)
[1] 300 300
# Call the hierarchical clustering function
geneTree = hclust(as.dist(dissTOM), method = "average");
# Plot the resulting clustering tree (dendrogram)
sizeGrWindow(12,9)
plot(geneTree, xlab="", sub="", 
     main = "Gene clustering of Synthetic Dataset",
     labels = FALSE, hang = 0.04)

# We like large modules, so we set the minimum module size relatively high:
minModuleSize = 10;

# Module identification using dynamic tree cut:
dynamicMods = cutreeDynamic(dendro = geneTree, distM = dissTOM,
                            deepSplit = 2, pamRespectsDendro = FALSE,
                            minClusterSize = minModuleSize)
 ..cutHeight not given, setting it to 0.809  ===>  99% of the (truncated) height range in dendro.
 ..done.
table(dynamicMods)
dynamicMods
 1  2  3  4  5  6  7  8  9 10 
30 30 30 30 30 30 30 30 30 30 
# Convert numeric lables into colors
dynamicColors = labels2colors(dynamicMods)
table(dynamicColors)
dynamicColors
    black      blue     brown     green   magenta      pink    purple       red turquoise    yellow 
       30        30        30        30        30        30        30        30        30        30 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)
plotDendroAndColors(geneTree, dynamicColors, "Dynamic Tree Cut",
dendroLabels = FALSE, hang = 0.03,
addGuide = TRUE, guideHang = 0.05,
main = "Gene dendrogram of Synthetic Dataset")

# We like large modules, so we set the minimum module size relatively high:
minModuleSize = 10;

# Module identification using dynamic tree cut:
dynamicMods = cutreeDynamic(dendro = geneTree, distM = dissTOM,
                            deepSplit = 2, pamRespectsDendro = FALSE,
                            minClusterSize = minModuleSize)
 ..cutHeight not given, setting it to 0.809  ===>  99% of the (truncated) height range in dendro.
 ..done.
table(dynamicMods)
dynamicMods
 1  2  3  4  5  6  7  8  9 10 
30 30 30 30 30 30 30 30 30 30 
# Convert numeric lables into colors
dynamicColors = labels2colors(dynamicMods)
table(dynamicColors)
dynamicColors
    black      blue     brown     green   magenta      pink    purple       red turquoise    yellow 
       30        30        30        30        30        30        30        30        30        30 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)
plotDendroAndColors(geneTree, dynamicColors, "Dynamic Tree Cut",
dendroLabels = FALSE, hang = 0.03,
addGuide = TRUE, guideHang = 0.05,
main = "Gene dendrogram of Synthetic Dataset")

datTraits = pData(phenData)
datTraits$Condition <- ifelse(datTraits$Condition == "Healthy", 0, 1)
datTraits
NA
library(ggplot2)

datExpr = ggset
nGenes = ncol(datExpr)
nSamples = nrow(datExpr)
datTraits = pData(eSet.norm)
datTraits$Condition <- ifelse(datTraits$Condition == "Healthy", 0, 1)

MEs0 = moduleEigengenes(datExpr, dynamicColors)$eigengenes
MEs = orderMEs(MEs0)
moduleTraitCor = cor(MEs, datTraits, use = "p")
moduleTraitPvalue = corPvalueStudent(moduleTraitCor, nSamples)


# Will display correlations and their p-values
png(paste0("WGCNAModules_Synthetic_", variability, ".png"),width=7,height=5,units="in",res=600)
textMatrix = paste(signif(moduleTraitCor, 2), "\n(",
                    signif(moduleTraitPvalue, 1), ")", sep = "");
                    dim(textMatrix) = dim(moduleTraitCor)
                    par(mar = c(6, 10, 3, 3))
par(mar = c(8, 8, 2, 1))  # Reduce margins (bottom, left, top, right)
labeledHeatmap(Matrix = moduleTraitCor,
              xLabels = names(datTraits),
              yLabels = names(MEs),
              ySymbols = names(MEs),
              colorLabels = FALSE,
              colors = blueWhiteRed(50),
              textMatrix = textMatrix,
              setStdMargins = FALSE,
              cex.text = 0.3,
              zlim = c(-1,1),
              main = paste("Diseased vs Healthy"))

C. Our Method

healthy.exprs = exprs(eSet.norm)[,1:50]
write.table(healthy.exprs,file = paste0("synthetic_healthy_", log_module_effect_magnitude,".txt"), sep = "\t", 
              quote = FALSE, row.names = TRUE, col.names = TRUE)

diseased.exprs = exprs(eSet.norm)[,51:100]
write.table(diseased.exprs, file = paste0("synthetic_diseased_", log_module_effect_magnitude,".txt"), sep = "\t", 
              quote = FALSE, row.names = TRUE, col.names = TRUE)
dim(healthy.exprs)
[1] 300  50
dim(diseased.exprs)
[1] 300  50

C.1 Low variability (p = 0.1)

healthy.dcor = read.csv("dcor-method/dcor_synthetic_healthy_0.1.csv", header=T)
healthy.dcor = as.matrix(healthy.dcor[,-c(1)])
dim(healthy.dcor)
[1] 300 300
TOM.healthy.dcor = TOMsimilarity(as.matrix(healthy.dcor));
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
dissTOM.healthy.dcor = 1-TOM.healthy.dcor
dim(TOM.healthy.dcor)
[1] 300 300
hist(rowSums(TOM.healthy.dcor))

write.table(dissTOM.healthy.dcor, file = "dcor-method/dissTOM_healthy_dcor9_0.1.txt", row.names = FALSE, col.names = FALSE, sep = " ")
# Call the hierarchical clustering function
geneTree = hclust(as.dist(dissTOM.healthy.dcor), method = "average");
# Plot the resulting clustering tree (dendrogram)
sizeGrWindow(12,9)
plot(geneTree, xlab="", sub="", 
     main = "Gene clustering of Healthy Samples based on TOM-based dissimilarity",
     labels = FALSE)

# We like large modules, so we set the minimum module size relatively high:
minModuleSize = 10;

# Module identification using dynamic tree cut:
dynamicMods = cutreeDynamic(dendro = geneTree, distM = dissTOM.healthy.dcor,
                            deepSplit = 2, pamRespectsDendro = FALSE,
                            minClusterSize = minModuleSize)
 ..cutHeight not given, setting it to 0.745  ===>  99% of the (truncated) height range in dendro.
 ..done.
table(dynamicMods)
dynamicMods
 1  2  3  4  5  6  7  8  9 10 
32 32 31 31 30 30 30 30 28 26 
# Convert numeric lables into colors
dynamicColors = labels2colors(dynamicMods)
table(dynamicColors)
dynamicColors
    black      blue     brown     green   magenta      pink    purple       red turquoise    yellow 
       30        32        31        30        28        30        26        30        32        31 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)

plotDendroAndColors(geneTree, dynamicColors, "Dynamic Tree Cut",
dendroLabels = FALSE, hang = 0.03,
addGuide = TRUE, guideHang = 0.05,
main = "Gene dendrogram of HealthySamples")

healthy.ggset = read.table(paste0("dcor-method/","synthetic_healthy_0.1.txt"), header = TRUE, sep="\t")
rownames(healthy.ggset) = healthy.ggset$Genes
healthy.ggset = as.data.frame(t(healthy.ggset[,-1]))
healthy.ggset = healthy.ggset[,colnames(healthy.dcor)]
head(healthy.ggset)

# Calculate eigengenes
MEList = moduleEigengenes(as.matrix(healthy.ggset), colors = dynamicColors)
MEs = MEList$eigengenes
# Calculate dissimilarity of module eigengenes
MEDiss = 1-cor(MEs);
# Cluster module eigengenes
METree = hclust(as.dist(MEDiss), method = "average");
# Plot the result
sizeGrWindow(16, 8)
plot(METree, main = "Clustering of module eigengenes in Healthy Samples",
xlab = "", sub = "")
# Merging modules
MEDissThres = 0.2
# Plot the cut line into the dendrogram
abline(h=MEDissThres, col = "red")



# Rename to moduleColors
moduleColors = dynamicColors
# Construct numerical labels corresponding to the colors
colorOrder = c("grey", standardColors(50));
moduleLabels = match(moduleColors, colorOrder)-1;
dim(MEs)
[1] 50 10
freq.tab <- as.data.frame(table(moduleColors))

colnames(freq.tab) <- c("Modules", "Membership")
freq.tab = freq.tab[order(-freq.tab$Membership), ]
rownames(freq.tab) <- 1:nrow(freq.tab)
freq.tab$Modules = factor(freq.tab$Modules, levels = freq.tab$Modules)

ggplot(freq.tab, aes(x = Modules, y = Membership, fill = Modules)) +
  geom_bar(stat = "identity",  color = "grey1", size = 0.5) +
  labs(title = "Module Size in Healthy Patients",
       x = "Modules",
       y = "Membership") +
  scale_fill_manual(values=as.character(freq.tab$Modules)) + # Set bar colors
  theme_minimal() + 
  theme(
    panel.grid = element_blank(),        # Remove grid lines
    axis.line = element_line(size = 0.5),  # Adjust line width of axes
    axis.text.x = element_text(size = 10, color='black', angle = 90), # Adjust font size of axis text
    axis.text.y = element_text(size = 10, color='black'),
    axis.title = element_text(size = 16) # Adjust font size of axis labels
  ) +
  theme(legend.position = "none")  # Remove legend if unnecessary

# Function to extract genes belonging to each module
genelist = names(healthy.ggset)
get_module_genes <- function(genelist, moduleColors) {
  unique_colors <- unique(moduleColors) # Get unique module names
  module_genes <- lapply(unique_colors, function(color) {
    which(moduleColors == color) # Indices of genes in this module
  })
  names(module_genes) <- unique_colors
  # Map indices back to gene names
  module_genes <- lapply(module_genes, function(indices) genelist[indices])
  return(module_genes)
}

healthy_modules <- get_module_genes(colnames(healthy.dcor), moduleColors)

# Create a dataframe
df <- data.frame(
  genes = colnames(healthy.dcor),
  modules = moduleColors
)
# Save the dataframe to a CSV file
output_file <- paste0("dcor-method/","modules_healthy_0.1.csv")
write.csv(df, file = output_file, row.names = F)
length(df$genes)
[1] 300
unique(df$modules)
 [1] "magenta"   "blue"      "yellow"    "turquoise" "red"       "purple"    "brown"     "black"     "green"    
[10] "pink"     
head(df)
diseased.dcor = read.csv("dcor-method/dcor_synthetic_diseased_0.1.csv", header=T)
diseased.dcor = as.matrix(diseased.dcor[,-c(1)])
dim(diseased.dcor)
[1] 300 300
TOM.diseased.dcor = TOMsimilarity(as.matrix(diseased.dcor));
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
dissTOM.diseased.dcor = 1-TOM.diseased.dcor
dim(TOM.diseased.dcor)
[1] 300 300
hist(rowSums(TOM.diseased.dcor))

write.table(dissTOM.diseased.dcor, file = "dcor-method/dissTOM_diseased_dcor9_0.1.txt", row.names = FALSE, col.names = FALSE, sep = " ")
# Call the hierarchical clustering function
geneTree2 = hclust(as.dist(dissTOM.diseased.dcor), method = "average");
# Plot the resulting clustering tree (dendrogram)
sizeGrWindow(12,9)
plot(geneTree2, xlab="", sub="", 
     main = "Gene clustering of Healthy Samples based on TOM-based dissimilarity",
     labels = FALSE)

# We like large modules, so we set the minimum module size relatively high:
minModuleSize = 10;

# Module identification using dynamic tree cut:
dynamicMods2 = cutreeDynamic(dendro = geneTree2, distM = dissTOM.diseased.dcor,
                            deepSplit = 2, pamRespectsDendro = FALSE,
                            minClusterSize = minModuleSize)
 ..cutHeight not given, setting it to 0.743  ===>  99% of the (truncated) height range in dendro.
 ..done.
table(dynamicMods2)
dynamicMods2
 1  2  3  4  5  6  7  8  9 10 
31 31 30 30 30 30 30 30 30 28 
# Convert numeric lables into colors
dynamicColors2 = labels2colors(dynamicMods2)
table(dynamicColors2)
dynamicColors2
    black      blue     brown     green   magenta      pink    purple       red turquoise    yellow 
       30        31        30        30        30        30        28        30        31        30 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)

plotDendroAndColors(geneTree2, dynamicColors2, "Dynamic Tree Cut",
dendroLabels = FALSE, hang = 0.03,
addGuide = TRUE, guideHang = 0.05,
main = "Gene dendrogram of Diseased Samples")

diseased.ggset = read.table(paste0("dcor-method/","synthetic_diseased_0.1.txt"), header = TRUE, sep="\t")
rownames(diseased.ggset) = diseased.ggset$Genes
diseased.ggset = as.data.frame(t(diseased.ggset[,-1]))
diseased.ggset = diseased.ggset[,colnames(diseased.dcor)]
head(diseased.ggset)

# Calculate eigengenes
MEList2 = moduleEigengenes(as.matrix(diseased.ggset), colors = dynamicColors2)
MEs2 = MEList2$eigengenes
# Calculate dissimilarity of module eigengenes
MEDiss2 = 1-cor(MEs2);
# Cluster module eigengenes
METree2 = hclust(as.dist(MEDiss2), method = "average");
# Plot the result
sizeGrWindow(16, 8)
plot(METree2, main = "Clustering of module eigengenes in Healthy Samples",
xlab = "", sub = "")
# Merging modules
MEDissThres = 0.2
# Plot the cut line into the dendrogram
abline(h=MEDissThres, col = "red")



# Rename to moduleColors
moduleColors2 = dynamicColors2
# Construct numerical labels corresponding to the colors
colorOrder2 = c("grey", standardColors(50));
moduleLabels2 = match(moduleColors2, colorOrder2)-1;
dim(MEs2)
[1] 50 10
freq.tab <- as.data.frame(table(moduleColors2))

colnames(freq.tab) <- c("Modules", "Membership")
freq.tab = freq.tab[order(-freq.tab$Membership), ]
rownames(freq.tab) <- 1:nrow(freq.tab)
freq.tab$Modules = factor(freq.tab$Modules, levels = freq.tab$Modules)

ggplot(freq.tab, aes(x = Modules, y = Membership, fill = Modules)) +
  geom_bar(stat = "identity",  color = "grey1", size = 0.5) +
  labs(title = "Module Size in Healthy Patients",
       x = "Modules",
       y = "Membership") +
  scale_fill_manual(values=as.character(freq.tab$Modules)) + # Set bar colors
  theme_minimal() + 
  theme(
    panel.grid = element_blank(),        # Remove grid lines
    axis.line = element_line(size = 0.5),  # Adjust line width of axes
    axis.text.x = element_text(size = 10, color='black', angle = 90), # Adjust font size of axis text
    axis.text.y = element_text(size = 10, color='black'),
    axis.title = element_text(size = 16) # Adjust font size of axis labels
  ) +
  theme(legend.position = "none")  # Remove legend if unnecessary

# Function to extract genes belonging to each module
genelist2 = names(diseased.ggset)
get_module_genes <- function(genelist, moduleColors) {
  unique_colors <- unique(moduleColors) # Get unique module names
  module_genes <- lapply(unique_colors, function(color) {
    which(moduleColors == color) # Indices of genes in this module
  })
  names(module_genes) <- unique_colors
  # Map indices back to gene names
  module_genes <- lapply(module_genes, function(indices) genelist[indices])
  return(module_genes)
}

diseased_modules <- get_module_genes(colnames(diseased.dcor), moduleColors2)

# Create a dataframe
df <- data.frame(
  genes = colnames(diseased.dcor),
  modules = moduleColors2
)
# Save the dataframe to a CSV file
output_file <- paste0("dcor-method/","modules_diseased_0.1.csv")
write.csv(df, file = output_file, row.names = F)
length(df$genes)
[1] 300
unique(df$modules)
 [1] "purple"    "blue"      "turquoise" "black"     "brown"     "red"       "magenta"   "green"     "pink"     
[10] "yellow"   
head(df)

C.2 High variability (p=0.9)

healthy.dcor = read.csv("dcor-method/dcor_synthetic_healthy_0.9.csv", header=T)
healthy.dcor = as.matrix(healthy.dcor[,-c(1)])
dim(healthy.dcor)
[1] 300 300
hist(rowSums(as.matrix(healthy.dcor)))

adj = adjacency.fromSimilarity(as.matrix(healthy.dcor), power=1)
hist(rowSums(adj))

TOM.healthy.dcor = TOMsimilarity(adj);
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
dissTOM.healthy.dcor = 1-TOM.healthy.dcor
dim(TOM.healthy.dcor)
[1] 300 300
hist(rowSums(TOM.healthy.dcor))

write.table(dissTOM.healthy.dcor, file = "dcor-method/dissTOM_healthy_dcor9_0.9.txt", row.names = FALSE, col.names = FALSE, sep = " ")
# Call the hierarchical clustering function
geneTree = hclust(as.dist(dissTOM.healthy.dcor), method = "average");
# Plot the resulting clustering tree (dendrogram)
sizeGrWindow(12,9)
plot(geneTree, xlab="", sub="", 
     main = "Gene clustering of Healthy Samples based on TOM-based dissimilarity",
     labels = FALSE)

# We like large modules, so we set the minimum module size relatively high:
minModuleSize = 10;

# Module identification using dynamic tree cut:
dynamicMods = cutreeDynamic(dendro = geneTree, distM = dissTOM.healthy.dcor,
                            deepSplit = 2, pamRespectsDendro = FALSE,
                            minClusterSize = minModuleSize)
 ..cutHeight not given, setting it to 0.742  ===>  99% of the (truncated) height range in dendro.
 ..done.
table(dynamicMods)
dynamicMods
 1  2  3  4  5  6  7  8 
76 44 35 33 30 29 29 24 
# Convert numeric lables into colors
dynamicColors = labels2colors(dynamicMods)
table(dynamicColors)
dynamicColors
    black      blue     brown     green      pink       red turquoise    yellow 
       29        44        35        30        24        29        76        33 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)

plotDendroAndColors(geneTree, dynamicColors, "Dynamic Tree Cut",
dendroLabels = FALSE, hang = 0.03,
addGuide = TRUE, guideHang = 0.05,
main = "Gene dendrogram of HealthySamples")

healthy.ggset = read.table(paste0("dcor-method/","synthetic_healthy_0.9.txt"), header = TRUE, sep="\t")
rownames(healthy.ggset) = healthy.ggset$Genes
healthy.ggset = as.data.frame(t(healthy.ggset[,-1]))
healthy.ggset = healthy.ggset[,colnames(healthy.dcor)]
head(healthy.ggset)

# Calculate eigengenes
MEList = moduleEigengenes(as.matrix(healthy.ggset), colors = dynamicColors)
MEs = MEList$eigengenes
# Calculate dissimilarity of module eigengenes
MEDiss = 1-cor(MEs);
# Cluster module eigengenes
METree = hclust(as.dist(MEDiss), method = "average");
# Plot the result
sizeGrWindow(16, 8)
plot(METree, main = "Clustering of module eigengenes in Healthy Samples",
xlab = "", sub = "")
# Merging modules
MEDissThres = 0.2
# Plot the cut line into the dendrogram
abline(h=MEDissThres, col = "red")



# Rename to moduleColors
moduleColors = dynamicColors
# Construct numerical labels corresponding to the colors
colorOrder = c("grey", standardColors(50));
moduleLabels = match(moduleColors, colorOrder)-1;
dim(MEs)
[1] 50  8
freq.tab <- as.data.frame(table(moduleColors))

colnames(freq.tab) <- c("Modules", "Membership")
freq.tab = freq.tab[order(-freq.tab$Membership), ]
rownames(freq.tab) <- 1:nrow(freq.tab)
freq.tab$Modules = factor(freq.tab$Modules, levels = freq.tab$Modules)

ggplot(freq.tab, aes(x = Modules, y = Membership, fill = Modules)) +
  geom_bar(stat = "identity",  color = "grey1", size = 0.5) +
  labs(title = "Module Size in Healthy Patients",
       x = "Modules",
       y = "Membership") +
  scale_fill_manual(values=as.character(freq.tab$Modules)) + # Set bar colors
  theme_minimal() + 
  theme(
    panel.grid = element_blank(),        # Remove grid lines
    axis.line = element_line(size = 0.5),  # Adjust line width of axes
    axis.text.x = element_text(size = 10, color='black', angle = 90), # Adjust font size of axis text
    axis.text.y = element_text(size = 10, color='black'),
    axis.title = element_text(size = 16) # Adjust font size of axis labels
  ) +
  theme(legend.position = "none")  # Remove legend if unnecessary

# Function to extract genes belonging to each module
genelist = names(healthy.ggset)
get_module_genes <- function(genelist, moduleColors) {
  unique_colors <- unique(moduleColors) # Get unique module names
  module_genes <- lapply(unique_colors, function(color) {
    which(moduleColors == color) # Indices of genes in this module
  })
  names(module_genes) <- unique_colors
  # Map indices back to gene names
  module_genes <- lapply(module_genes, function(indices) genelist[indices])
  return(module_genes)
}

healthy_modules <- get_module_genes(colnames(healthy.dcor), moduleColors)

# Create a dataframe
df <- data.frame(
  genes = colnames(healthy.dcor),
  modules = moduleColors
)
# Save the dataframe to a CSV file
output_file <- paste0("dcor-method/","modules_healthy_0.9.csv")
write.csv(df, file = output_file, row.names = F)
length(df$genes)
[1] 300
unique(df$modules)
[1] "turquoise" "brown"     "blue"      "yellow"    "green"     "pink"      "black"     "red"      
head(df)
diseased.dcor = read.csv("dcor-method/dcor_synthetic_diseased_0.9.csv", header=T)
diseased.dcor = as.matrix(diseased.dcor[,-c(1)])
dim(diseased.dcor)
[1] 300 300
hist(rowSums(as.matrix(diseased.dcor)))

adj = adjacency.fromSimilarity(as.matrix(diseased.dcor), power=1)
hist(rowSums(adj))

TOM.diseased.dcor = TOMsimilarity(adj);
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
dissTOM.diseased.dcor = 1-TOM.diseased.dcor
dim(TOM.diseased.dcor)
[1] 300 300
hist(rowSums(TOM.diseased.dcor))

write.table(dissTOM.diseased.dcor, file = "dcor-method/dissTOM_diseased_dcor9_0.9.txt", row.names = FALSE, col.names = FALSE, sep = " ")
# Call the hierarchical clustering function
geneTree2 = hclust(as.dist(dissTOM.diseased.dcor), method = "average");
# Plot the resulting clustering tree (dendrogram)
sizeGrWindow(12,9)
plot(geneTree2, xlab="", sub="", 
     main = "Gene clustering of Healthy Samples based on TOM-based dissimilarity",
     labels = FALSE)

# We like large modules, so we set the minimum module size relatively high:
minModuleSize = 10;

# Module identification using dynamic tree cut:
dynamicMods2 = cutreeDynamic(dendro = geneTree2, distM = dissTOM.diseased.dcor,
                            deepSplit = 2, pamRespectsDendro = FALSE,
                            minClusterSize = minModuleSize)
 ..cutHeight not given, setting it to 0.741  ===>  99% of the (truncated) height range in dendro.
 ..done.
table(dynamicMods2)
dynamicMods2
 1  2  3  4  5  6  7  8  9 
63 36 33 30 30 29 27 27 25 
# Convert numeric lables into colors
dynamicColors2 = labels2colors(dynamicMods2)
table(dynamicColors2)
dynamicColors2
    black      blue     brown     green   magenta      pink       red turquoise    yellow 
       27        36        33        30        25        27        29        63        30 
# Plot the dendrogram and colors underneath
sizeGrWindow(8,6)

plotDendroAndColors(geneTree2, dynamicColors2, "Dynamic Tree Cut",
dendroLabels = FALSE, hang = 0.03,
addGuide = TRUE, guideHang = 0.05,
main = "Gene dendrogram of Diseased Samples")

diseased.ggset = read.table(paste0("dcor-method/","synthetic_diseased_0.9.txt"), header = TRUE, sep="\t")
rownames(diseased.ggset) = diseased.ggset$Genes
diseased.ggset = as.data.frame(t(diseased.ggset[,-1]))
diseased.ggset = diseased.ggset[,colnames(diseased.dcor)]
head(diseased.ggset)

# Calculate eigengenes
MEList2 = moduleEigengenes(as.matrix(diseased.ggset), colors = dynamicColors2)
MEs2 = MEList2$eigengenes
# Calculate dissimilarity of module eigengenes
MEDiss2 = 1-cor(MEs2);
# Cluster module eigengenes
METree2 = hclust(as.dist(MEDiss2), method = "average");
# Plot the result
sizeGrWindow(16, 8)
plot(METree2, main = "Clustering of module eigengenes in Healthy Samples",
xlab = "", sub = "")
# Merging modules
MEDissThres = 0.2
# Plot the cut line into the dendrogram
abline(h=MEDissThres, col = "red")



# Rename to moduleColors
moduleColors2 = dynamicColors2
# Construct numerical labels corresponding to the colors
colorOrder2 = c("grey", standardColors(50));
moduleLabels2 = match(moduleColors2, colorOrder2)-1;
dim(MEs2)
[1] 50  9
freq.tab <- as.data.frame(table(moduleColors2))

colnames(freq.tab) <- c("Modules", "Membership")
freq.tab = freq.tab[order(-freq.tab$Membership), ]
rownames(freq.tab) <- 1:nrow(freq.tab)
freq.tab$Modules = factor(freq.tab$Modules, levels = freq.tab$Modules)

ggplot(freq.tab, aes(x = Modules, y = Membership, fill = Modules)) +
  geom_bar(stat = "identity",  color = "grey1", size = 0.5) +
  labs(title = "Module Size in Healthy Patients",
       x = "Modules",
       y = "Membership") +
  scale_fill_manual(values=as.character(freq.tab$Modules)) + # Set bar colors
  theme_minimal() + 
  theme(
    panel.grid = element_blank(),        # Remove grid lines
    axis.line = element_line(size = 0.5),  # Adjust line width of axes
    axis.text.x = element_text(size = 10, color='black', angle = 90), # Adjust font size of axis text
    axis.text.y = element_text(size = 10, color='black'),
    axis.title = element_text(size = 16) # Adjust font size of axis labels
  ) +
  theme(legend.position = "none")  # Remove legend if unnecessary

D. DGCA

# renv::install("andymckenzie/DGCA")
library(DGCA)

D.1 Low variability (p=0.1)

log_module_effect_magnitude = 0.1
dgca_design <- data.frame(
  Healthy = as.numeric(!datTraits$Condition),
  Diseased = as.numeric(datTraits$Condition))
rownames(dgca_design) <- rownames(datTraits)

synthetic_expression_data = read.csv("synthetic_expression_data_effect_0.1.csv", header=T, row.names = "X")
modules_list = read.csv("wgcna/moduleColors_WGCNA_0.1.csv", header=T)

moduleDC_res <- moduleDC(inputMat = synthetic_expression_data,
                      design = as.matrix(dgca_design),
                      compare = c("Healthy","Diseased"), # Column name in dgca_design
                      genes = modules_list$genes,
                      labels = modules_list$modules,
                      nPerms = 50,
                      number_DC_genes = 10,
                      dCorAvgMethod = "median")
Calculating MDC for module #1, which is called grey
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #2, which is called green
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #3, which is called turquoise
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #4, which is called black
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #5, which is called brown
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #6, which is called magenta
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #7, which is called purple
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #8, which is called yellow
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #9, which is called red
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #10, which is called blue
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #11, which is called pink
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
moduleDC_res

# 1. Extract and prepare data
module_raw_names <- moduleDC_res$Module # e.g., c("blue", "red", "green")
module_meanDCs <- moduleDC_res$MeDC    # e.g., c(0.1, -0.2, 0.05)
module_pVals <- moduleDC_res$pVal      # e.g., c(0.001, 0.05, 0.1)

# 2. Create the desired module order with "ME" prefix
module_order <- paste0("ME", module_raw_names) # This will be MEblue, MEred, MEgreen

# 3. Create the heatmap matrix and assign meaningful row names
dgca_heatmap_matrix <- as.matrix(module_meanDCs)
# Assign the new module names as row names for the matrix.
# This is crucial for labeledHeatmap to correctly associate labels with rows.
rownames(dgca_heatmap_matrix) <- module_order
colnames(dgca_heatmap_matrix) <- c("Mean Diff. Cor.") # This column name will be for xLabel

# 4. Prepare text matrix for p-values and meanDCs
dgca_textMatrix = paste(signif(dgca_heatmap_matrix, 2), "\n(",
                        signif(module_pVals, 1), ")", sep = "");
dim(dgca_textMatrix) = dim(dgca_heatmap_matrix) # Ensure dimensions are correctly set

# Plotting
cat("\n--- DGCA Pre-defined Module-level Differential Correlation Heatmap ---\n")

--- DGCA Pre-defined Module-level Differential Correlation Heatmap ---
sizeGrWindow(6, 8) # Adjust window size for plot

# Define output file path and name
output_png_file <- paste0("dcga/DGCA_Module_MeanDC_Heatmap_Effect_", log_module_effect_magnitude, ".png")

# Ensure the 'dcga' directory exists. If not, create it.
if (!dir.exists("dcga")) {
  dir.create("dcga")
}

# # Open PNG device to save the plot
# png(output_png_file, width = 600, height = 800, res=100, bg = "white")
# 
# # Adjust plot margins (bottom, left, top, right)
# # Increase left margin to ensure enough space for longer module names
# par(mar = c(6, 12, 3, 3))
# 
# labeledHeatmap(Matrix = dgca_heatmap_matrix,
#                xLabels = "Condition",           # Overall label for the x-axis
#                yLabels = module_order,        # Overall label for the y-axis
#                ySymbols = rownames(dgca_heatmap_matrix), # Individual labels for each row (MEblue, MEred, etc.)
#                colorLabels = FALSE,
#                colors = blueWhiteRed(50),       # Color scale (from WGCNA)
#                textMatrix = dgca_textMatrix,    # Text to display inside heatmap cells
#                setStdMargins = FALSE,
#                cex.text = 0.7,                  # Size of text INSIDE the cells (meanDC (p-val))
#                cex.lab = 1,                     # Size of "Condition" and "Module Names" overall labels
#                cex.rowLabels = 0.9,             # **CRITICAL**: Size of the individual row labels (ySymbols). Adjust as needed.
#                zlim = c(-max(abs(dgca_heatmap_matrix)), max(abs(dgca_heatmap_matrix))), # Symmetric color scale
#                main = paste("DGCA: Median Within-Module Diff")) # Main plot title
# 


# Plotting the heatmap using labeledHeatmap
# Customize text matrix for correlation and p-value display
dev.off()
null device 
          1 
png(output_png_file,width=6,height=5,units="in",res=600)
textMatrix = paste(signif(moduleTraitCor, 2), "\n(",
                    signif(moduleTraitPvalue, 1), ")", sep = "");
                    dim(textMatrix) = dim(moduleTraitCor)
                    par(mar = c(6, 10, 3, 3))
par(mar = c(8, 8, 2, 1))  # Reduce margins (bottom, left, top, right)
labeledHeatmap(Matrix = dgca_heatmap_matrix,
               xLabels = "Condition",
               yLabels = module_order,
               ySymbols = rownames(dgca_heatmap_matrix),
               colorLabels = FALSE,
               colors = blueWhiteRed(50), # Red for positive, blue for negative correlation
               textMatrix = dgca_textMatrix,
               setStdMargins = FALSE,
               cex.text = 0.5, # Adjust text size
               # zlim = c(-1, 1),
               main = paste("DGCA")
               )

cat("\nModule-Trait Relationship Heatmap generated.\n")

Module-Trait Relationship Heatmap generated.
dev.off()
null device 
          1 
cat(sprintf("Saved %s\n\n", output_png_file))
Saved dcga/DGCA_Module_MeanDC_Heatmap_Effect_0.1.png

D.2 High Variability (p=0.9)

log_module_effect_magnitude = 0.9
dgca_design <- data.frame(
  Healthy = as.numeric(!datTraits$Condition),
  Diseased = as.numeric(datTraits$Condition))
rownames(dgca_design) <- rownames(datTraits)

synthetic_expression_data = read.csv("synthetic_expression_data_effect_0.9.csv", header=T, row.names = "X")
modules_list = read.csv("wgcna/moduleColors_WGCNA_0.9.csv", header=T)

moduleDC_res <- moduleDC(inputMat = synthetic_expression_data,
                      design = as.matrix(dgca_design),
                      compare = c("Healthy","Diseased"), # Column name in dgca_design
                      genes = modules_list$genes,
                      labels = modules_list$modules,
                      nPerms = 50,
                      number_DC_genes = 10,
                      dCorAvgMethod = "median")
Calculating MDC for module #1, which is called brown
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #2, which is called yellow
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #3, which is called turquoise
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #4, which is called green
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #5, which is called pink
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #6, which is called red
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #7, which is called black
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #8, which is called blue
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #9, which is called magenta
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #10, which is called purple
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
Calculating MDC for module #11, which is called greenyellow
Calculating permutation number 1.
Calculating permutation number 2.
Calculating permutation number 3.
Calculating permutation number 4.
Calculating permutation number 5.
Calculating permutation number 6.
Calculating permutation number 7.
Calculating permutation number 8.
Calculating permutation number 9.
Calculating permutation number 10.
Calculating permutation number 11.
Calculating permutation number 12.
Calculating permutation number 13.
Calculating permutation number 14.
Calculating permutation number 15.
Calculating permutation number 16.
Calculating permutation number 17.
Calculating permutation number 18.
Calculating permutation number 19.
Calculating permutation number 20.
Calculating permutation number 21.
Calculating permutation number 22.
Calculating permutation number 23.
Calculating permutation number 24.
Calculating permutation number 25.
Calculating permutation number 26.
Calculating permutation number 27.
Calculating permutation number 28.
Calculating permutation number 29.
Calculating permutation number 30.
Calculating permutation number 31.
Calculating permutation number 32.
Calculating permutation number 33.
Calculating permutation number 34.
Calculating permutation number 35.
Calculating permutation number 36.
Calculating permutation number 37.
Calculating permutation number 38.
Calculating permutation number 39.
Calculating permutation number 40.
Calculating permutation number 41.
Calculating permutation number 42.
Calculating permutation number 43.
Calculating permutation number 44.
Calculating permutation number 45.
Calculating permutation number 46.
Calculating permutation number 47.
Calculating permutation number 48.
Calculating permutation number 49.
Calculating permutation number 50.
Classifying the differential correlation calls.
Calculating the differential correlation average.
Calculating the differential correlation average.
moduleDC_res
# 1. Extract and prepare data
module_raw_names <- moduleDC_res$Module # e.g., c("blue", "red", "green")
module_meanDCs <- moduleDC_res$MeDC    # e.g., c(0.1, -0.2, 0.05)
module_pVals <- moduleDC_res$pVal      # e.g., c(0.001, 0.05, 0.1)

# 2. Create the desired module order with "ME" prefix
module_order <- paste0("ME", module_raw_names) # This will be MEblue, MEred, MEgreen

# 3. Create the heatmap matrix and assign meaningful row names
dgca_heatmap_matrix <- as.matrix(module_meanDCs)
# Assign the new module names as row names for the matrix.
# This is crucial for labeledHeatmap to correctly associate labels with rows.
rownames(dgca_heatmap_matrix) <- module_order
colnames(dgca_heatmap_matrix) <- c("Mean Diff. Cor.") # This column name will be for xLabel

# 4. Prepare text matrix for p-values and meanDCs
dgca_textMatrix = paste(signif(dgca_heatmap_matrix, 2), "\n(",
                        signif(module_pVals, 1), ")", sep = "");
dim(dgca_textMatrix) = dim(dgca_heatmap_matrix) # Ensure dimensions are correctly set

# Plotting
cat("\n--- DGCA Pre-defined Module-level Differential Correlation Heatmap ---\n")

--- DGCA Pre-defined Module-level Differential Correlation Heatmap ---
sizeGrWindow(6, 8) # Adjust window size for plot

# Define output file path and name
output_png_file <- paste0("dcga/DGCA_Module_MeanDC_Heatmap_Effect_", log_module_effect_magnitude, ".png")

# Ensure the 'dcga' directory exists. If not, create it.
if (!dir.exists("dcga")) {
  dir.create("dcga")
}

dev.off()
null device 
          1 
png(output_png_file,width=6,height=5,units="in",res=600)
textMatrix = paste(signif(moduleTraitCor, 2), "\n(",
                    signif(moduleTraitPvalue, 1), ")", sep = "");
                    dim(textMatrix) = dim(moduleTraitCor)
                    par(mar = c(6, 10, 3, 3))
par(mar = c(8, 8, 2, 1))  # Reduce margins (bottom, left, top, right)
labeledHeatmap(Matrix = dgca_heatmap_matrix,
               xLabels = "Condition",
               yLabels = module_order,
               ySymbols = rownames(dgca_heatmap_matrix),
               colorLabels = FALSE,
               colors = blueWhiteRed(50), # Red for positive, blue for negative correlation
               textMatrix = dgca_textMatrix,
               setStdMargins = FALSE,
               cex.text = 0.5, # Adjust text size
               # zlim = c(-1, 1),
               main = paste("DGCA")
               )

cat("\nModule-Trait Relationship Heatmap generated.\n")

Module-Trait Relationship Heatmap generated.
dev.off()
null device 
          1 
cat(sprintf("Saved %s\n\n", output_png_file))
Saved dcga/DGCA_Module_MeanDC_Heatmap_Effect_0.9.png

E. DiffCoExp

# renv::install("diffcoexp")
# renv::install("GEOquery")
library(diffcoexp)
allowWGCNAThreads()
Allowing multi-threading with up to 8 threads.

E.1 Low variability (p=0.1)

exprs.h = read.csv("dcor-method/synthetic_healthy_0.1.txt", sep = "\t", header=T, row.names = "Genes")
exprs.d = read.csv("dcor-method/synthetic_diseased_0.1.txt", sep = "\t", header=T, row.names = "Genes")
allowWGCNAThreads()
Allowing multi-threading with up to 8 threads.
# Diseased - Healthy; high correlation --> diseased
# Step 1
res.low =diffcoexp(exprs.1 = exprs.d, exprs.2 = exprs.h,
                   r.method = "pearson",q.method = "bonferroni")
Finished running comparecor.
Finished running coexpr.
2484 gene pairs remain after half thresholding.
167 DCLs identified.
5 DCGs identified.
library(WGCNA)
# WGCNA often recommends setting this
options(stringsAsFactors = FALSE)
allowWGCNAThreads() # Use multiple cores if available
Allowing multi-threading with up to 8 threads.
# --- Input Data ---
# Read expression data for two conditions
# Ensure 'row.names = "Genes"' correctly identifies the gene ID column
exprs.1 = read.csv("dcor-method/synthetic_healthy_0.1.txt", sep = "\t", header=T, row.names = "Genes")
exprs.2 = read.csv("dcor-method/synthetic_diseased_0.1.txt", sep = "\t", header=T, row.names = "Genes")

# Ensure gene order is consistent across conditions
commonGenes <- intersect(rownames(exprs.1), rownames(exprs.2))
exprs.1 <- exprs.1[commonGenes, ]
exprs.2 <- exprs.2[commonGenes, ]

# IMPORTANT for WGCNA: Transpose data so samples are rows and genes are columns
datExpr1 <- as.data.frame(t(exprs.1))
datExpr2 <- as.data.frame(t(exprs.2))

# Combine expression data for module eigengene calculation and trait mapping
# The column names (genes) must be identical for cbind to work correctly
datExpr_combined <- t(cbind(exprs.1, exprs.2))

cat("Data dimensions for WGCNA (samples x genes):\n")
Data dimensions for WGCNA (samples x genes):
cat("Condition 1:", dim(datExpr1), "\n")
Condition 1: 50 300 
cat("Condition 2:", dim(datExpr2), "\n")
Condition 2: 50 300 
cat("Combined data:", dim(datExpr_combined), "\n\n")
Combined data: 100 300 
# --- Define Soft-thresholding Power (beta) ---
# As per the paper, beta is a tuning parameter for the adjacency difference matrix.
# Its choice impacts the stringency of differential correlation.
beta = 1 # Example value, adjust as needed based on your data and desired stringency


# ==============================================================================
# DiffCoEx Algorithm Steps
# ==============================================================================

# --- Step 1: Build correlation matrix C[k] within each condition k ---
cat("Step 1: Computing correlation matrices for each condition...\n")
Step 1: Computing correlation matrices for each condition...
corMatrix1 <- WGCNA::cor(datExpr1, method = "spearman", use = "pairwise.complete.obs")
corMatrix2 <- WGCNA::cor(datExpr2, method = "spearman", use = "pairwise.complete.obs")

cat("Correlation matrices computed.\n\n")
Correlation matrices computed.
# --- Step 2: Compute matrix of adjacency difference D ---
# d_ij = |sign(cor_ij^(1))(cor_ij^(1))^2 - sign(cor_ij^(2))(cor_ij^(2))^2|^beta
cat("Step 2: Computing the adjacency difference matrix D...\n")
Step 2: Computing the adjacency difference matrix D...
# Get all unique genes (should be commonGenes from above)
allGenes <- colnames(datExpr1)

# Initialize D matrix with zeros
D_matrix <- matrix(0,
                   nrow = length(allGenes),
                   ncol = length(allGenes),
                   dimnames = list(allGenes, allGenes))

# Loop through all gene pairs to calculate D_ij
for (i in 1:length(allGenes)) {
  for (j in i:length(allGenes)) { # Loop j from i for upper triangle (and symmetry)
    gene_i <- allGenes[i]
    gene_j <- allGenes[j]

    # Get correlations for the pair
    cor_ij_1 <- corMatrix1[gene_i, gene_j]
    cor_ij_2 <- corMatrix2[gene_i, gene_j]

    # Handle potential NA correlations
    # If a correlation is NA in either condition, set differential to 0 for that pair
    if (is.na(cor_ij_1) || is.na(cor_ij_2)) {
      D_matrix[gene_i, gene_j] <- 0
      D_matrix[gene_j, gene_i] <- 0
      next
    }

    # Calculate signed squared correlations
    s_ij_1 <- sign(cor_ij_1) * (cor_ij_1^2)
    s_ij_2 <- sign(cor_ij_2) * (cor_ij_2^2)

    # Calculate d_ij as per paper's formula: |s_ij_1 - s_ij_2|^beta
    d_ij_val <- abs(s_ij_1 - s_ij_2)^beta

    # Populate D_matrix symmetrically
    D_matrix[gene_i, gene_j] <- d_ij_val
    D_matrix[gene_j, gene_i] <- d_ij_val # Ensure symmetry
  }
}

cat("Adjacency difference matrix D computed. Its range is [", min(D_matrix), ", ", max(D_matrix), "]\n\n")
Adjacency difference matrix D computed. Its range is [ 0 ,  0.5624463 ]
# --- Step 3: Derive the Topological Overlap (TO) based dissimilarity matrix T from D ---
cat("Step 3: Computing Topological Overlap Dissimilarity (TOM) from D...\n")
Step 3: Computing Topological Overlap Dissimilarity (TOM) from D...
# It's good practice to ensure the diagonal is 1 for adjacency matrices before TOM
diag(D_matrix) <- 1 # Gene is perfectly "differentially coexpressed" with itself conceptually

# Use WGCNA's TOMsimilarity function on our custom differential adjacency matrix D.
# TOMType="unsigned" is generally appropriate for D_matrix values which are [0,1].
TOM_matrix <- TOMsimilarity(D_matrix, TOMType = "unsigned")
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
# Convert TOM to dissimilarity (1 - TOM)
T_dissimilarity <- 1 - TOM_matrix

cat("Topological Overlap Dissimilarity matrix T computed.\n\n")
Topological Overlap Dissimilarity matrix T computed.
# --- Step 4: Clustering and Module Identification ---
cat("Step 4: Performing hierarchical clustering and module identification...\n")
Step 4: Performing hierarchical clustering and module identification...
# Hierarchical clustering
geneTree_diffcoex <- hclust(as.dist(T_dissimilarity), method = "average")

# Plot the dendrogram
plot(geneTree_diffcoex, xlab = "", sub = "", main = "Gene dendrogram based on differential TOM",
     labels = FALSE, hang = 0.04)

# Module identification using dynamicTreeCut
minModuleSize_diffcoex <- 10 # Adjust based on your data and desired module size
dynamicMods_diffcoex <- cutreeDynamic(dendro = geneTree_diffcoex,
                                      distM = T_dissimilarity,
                                      deepSplit = 2, # Adjust deepSplit for granularity (0-4)
                                      pamRespectsDendro = FALSE, # Set to TRUE for PAM refinement
                                      minClusterSize = minModuleSize_diffcoex)
 ..cutHeight not given, setting it to 0.956  ===>  99% of the (truncated) height range in dendro.
 ..done.
# Assign module colors (for visualization and further analysis)
moduleColors_diffcoex <- labels2colors(dynamicMods_diffcoex)

cat("Module identification complete.\n")
Module identification complete.
cat("Number of differential co-expression modules found:", length(unique(moduleColors_diffcoex)) - 1,
    " (excluding grey for unassigned genes)\n") # -1 for 'grey' module
Number of differential co-expression modules found: 9  (excluding grey for unassigned genes)
cat("Sizes of modules:\n")
Sizes of modules:
print(table(moduleColors_diffcoex))
moduleColors_diffcoex
    black      blue     brown     green   magenta      pink    purple       red turquoise    yellow 
       30        31        31        30        29        29        28        30        32        30 
# Output the module assignments to a data frame
differential_modules <- data.frame(
  Gene = colnames(datExpr1),
  Module = moduleColors_diffcoex
)

# Save the dataframe to a CSV file
output_file <- "diffcoexp/moduleColors_synthetic_0.1.csv"
write.csv(differential_modules, file = output_file, row.names = F)
cat(paste0("\nModule assignments saved to ", output_file, "\n"))

Module assignments saved to diffcoexp/moduleColors_synthetic_0.1.csv
# --- New Section: Plotting Module-Trait Relationships ---

cat("\n--- Plotting Module-Trait Relationships ---\n")

--- Plotting Module-Trait Relationships ---
# Create a trait data frame for your samples
# The row names of the trait data frame must match the sample names in datExpr_combined
trait_data <- data.frame(
  Sample = c(rownames(datExpr1), rownames(datExpr2)),
  Condition = c(rep("Healthy", nrow(datExpr1)), rep("Diseased", nrow(datExpr2)))
)
rownames(trait_data) <- trait_data$Sample # Set sample names as row names

# Convert categorical trait to numeric for correlation (WGCNA expects numeric)
# 0 for Healthy, 1 for Diseased (arbitrary assignment)
trait_numeric <- as.data.frame(as.numeric(trait_data$Condition == "Diseased"))
rownames(trait_numeric) <- rownames(trait_data)
colnames(trait_numeric) <- "Diseased_vs_Healthy"

cat("Trait data prepared.\n")
Trait data prepared.
print(head(trait_numeric))
cat("\n")
# Calculate Module Eigengenes (MEs)
# MEs represent the "average" expression profile of each module.
# Use the combined expression data and the identified module colors.
MEs_diffcoex <- moduleEigengenes(datExpr_combined, moduleColors_diffcoex)$eigengenes
cat("Module Eigengenes calculated.\n")
Module Eigengenes calculated.
print(head(MEs_diffcoex))
cat("\n")
# Relate MEs to the trait (Condition)
# Calculate correlations and their p-values
moduleTraitCor <- WGCNA::cor(MEs_diffcoex, trait_numeric, use = "pairwise.complete.obs", method = "pearson") # Pearson is common for MEs
moduleTraitPvalue <- WGCNA::corPvalueStudent(moduleTraitCor, nrow(datExpr_combined)) # Number of samples

cat("Module-trait correlations and p-values calculated.\n\n")
Module-trait correlations and p-values calculated.
# Plotting the heatmap using labeledHeatmap
# Customize text matrix for correlation and p-value display
dev.off()
null device 
          1 
png("diffcoexp/DiffCoExp_Synthetic_0.1.png",width=6,height=5,units="in",res=600)

textMatrix = paste(signif(moduleTraitCor, 2), "\n(",
                    signif(moduleTraitPvalue, 1), ")", sep = "");
                    dim(textMatrix) = dim(moduleTraitCor)
                    par(mar = c(6, 10, 3, 3))
par(mar = c(8, 8, 2, 1))  # Reduce margins (bottom, left, top, right)
labeledHeatmap(Matrix = moduleTraitCor,
               xLabels = "Condition",
               yLabels = names(MEs_diffcoex),
               ySymbols = names(MEs_diffcoex),
               colorLabels = FALSE,
               colors = blueWhiteRed(50), # Red for positive, blue for negative correlation
               textMatrix = textMatrix,
               setStdMargins = FALSE,
               cex.text = 0.5, # Adjust text size
               zlim = c(-1, 1),
               main = paste("DiffCoEx Modules")
               )

cat("\nModule-Trait Relationship Heatmap generated.\n")

Module-Trait Relationship Heatmap generated.
dev.off()
null device 
          1 

E.2 High variability (p=0.9)

library(WGCNA)
# WGCNA often recommends setting this
options(stringsAsFactors = FALSE)
allowWGCNAThreads() # Use multiple cores if available
Allowing multi-threading with up to 8 threads.
# --- Input Data ---
# Read expression data for two conditions
# Ensure 'row.names = "Genes"' correctly identifies the gene ID column
exprs.1 = read.csv("dcor-method/synthetic_healthy_0.9.txt", sep = "\t", header=T, row.names = "Genes")
exprs.2 = read.csv("dcor-method/synthetic_diseased_0.9.txt", sep = "\t", header=T, row.names = "Genes")

# Ensure gene order is consistent across conditions
commonGenes <- intersect(rownames(exprs.1), rownames(exprs.2))
exprs.1 <- exprs.1[commonGenes, ]
exprs.2 <- exprs.2[commonGenes, ]

# IMPORTANT for WGCNA: Transpose data so samples are rows and genes are columns
datExpr1 <- as.data.frame(t(exprs.1))
datExpr2 <- as.data.frame(t(exprs.2))

# Combine expression data for module eigengene calculation and trait mapping
# The column names (genes) must be identical for cbind to work correctly
datExpr_combined <- t(cbind(exprs.1, exprs.2))

cat("Data dimensions for WGCNA (samples x genes):\n")
Data dimensions for WGCNA (samples x genes):
cat("Condition 1:", dim(datExpr1), "\n")
Condition 1: 50 300 
cat("Condition 2:", dim(datExpr2), "\n")
Condition 2: 50 300 
cat("Combined data:", dim(datExpr_combined), "\n\n")
Combined data: 100 300 
# --- Define Soft-thresholding Power (beta) ---
# As per the paper, beta is a tuning parameter for the adjacency difference matrix.
# Its choice impacts the stringency of differential correlation.
beta = 1 # Example value, adjust as needed based on your data and desired stringency


# ==============================================================================
# DiffCoEx Algorithm Steps
# ==============================================================================

# --- Step 1: Build correlation matrix C[k] within each condition k ---
cat("Step 1: Computing correlation matrices for each condition...\n")
Step 1: Computing correlation matrices for each condition...
corMatrix1 <- WGCNA::cor(datExpr1, method = "spearman", use = "pairwise.complete.obs")
corMatrix2 <- WGCNA::cor(datExpr2, method = "spearman", use = "pairwise.complete.obs")

cat("Correlation matrices computed.\n\n")
Correlation matrices computed.
# --- Step 2: Compute matrix of adjacency difference D ---
# d_ij = |sign(cor_ij^(1))(cor_ij^(1))^2 - sign(cor_ij^(2))(cor_ij^(2))^2|^beta
cat("Step 2: Computing the adjacency difference matrix D...\n")
Step 2: Computing the adjacency difference matrix D...
# Get all unique genes (should be commonGenes from above)
allGenes <- colnames(datExpr1)

# Initialize D matrix with zeros
D_matrix <- matrix(0,
                   nrow = length(allGenes),
                   ncol = length(allGenes),
                   dimnames = list(allGenes, allGenes))

# Loop through all gene pairs to calculate D_ij
for (i in 1:length(allGenes)) {
  for (j in i:length(allGenes)) { # Loop j from i for upper triangle (and symmetry)
    gene_i <- allGenes[i]
    gene_j <- allGenes[j]

    # Get correlations for the pair
    cor_ij_1 <- corMatrix1[gene_i, gene_j]
    cor_ij_2 <- corMatrix2[gene_i, gene_j]

    # Handle potential NA correlations
    # If a correlation is NA in either condition, set differential to 0 for that pair
    if (is.na(cor_ij_1) || is.na(cor_ij_2)) {
      D_matrix[gene_i, gene_j] <- 0
      D_matrix[gene_j, gene_i] <- 0
      next
    }

    # Calculate signed squared correlations
    s_ij_1 <- sign(cor_ij_1) * (cor_ij_1^2)
    s_ij_2 <- sign(cor_ij_2) * (cor_ij_2^2)

    # Calculate d_ij as per paper's formula: |s_ij_1 - s_ij_2|^beta
    d_ij_val <- abs(s_ij_1 - s_ij_2)^beta

    # Populate D_matrix symmetrically
    D_matrix[gene_i, gene_j] <- d_ij_val
    D_matrix[gene_j, gene_i] <- d_ij_val # Ensure symmetry
  }
}

cat("Adjacency difference matrix D computed. Its range is [", min(D_matrix), ", ", max(D_matrix), "]\n\n")
Adjacency difference matrix D computed. Its range is [ 0 ,  0.5001252 ]
# --- Step 3: Derive the Topological Overlap (TO) based dissimilarity matrix T from D ---
cat("Step 3: Computing Topological Overlap Dissimilarity (TOM) from D...\n")
Step 3: Computing Topological Overlap Dissimilarity (TOM) from D...
# It's good practice to ensure the diagonal is 1 for adjacency matrices before TOM
diag(D_matrix) <- 1 # Gene is perfectly "differentially coexpressed" with itself conceptually

# Use WGCNA's TOMsimilarity function on our custom differential adjacency matrix D.
# TOMType="unsigned" is generally appropriate for D_matrix values which are [0,1].
TOM_matrix <- TOMsimilarity(D_matrix, TOMType = "unsigned")
..connectivity..
..matrix multiplication (system BLAS)..
..normalization..
..done.
# Convert TOM to dissimilarity (1 - TOM)
T_dissimilarity <- 1 - TOM_matrix

cat("Topological Overlap Dissimilarity matrix T computed.\n\n")
Topological Overlap Dissimilarity matrix T computed.
# --- Step 4: Clustering and Module Identification ---
cat("Step 4: Performing hierarchical clustering and module identification...\n")
Step 4: Performing hierarchical clustering and module identification...
# Hierarchical clustering
geneTree_diffcoex <- hclust(as.dist(T_dissimilarity), method = "average")

# Plot the dendrogram
plot(geneTree_diffcoex, xlab = "", sub = "", main = "Gene dendrogram based on differential TOM",
     labels = FALSE, hang = 0.04)

# Module identification using dynamicTreeCut
minModuleSize_diffcoex <- 10 # Adjust based on your data and desired module size
dynamicMods_diffcoex <- cutreeDynamic(dendro = geneTree_diffcoex,
                                      distM = T_dissimilarity,
                                      deepSplit = 2, # Adjust deepSplit for granularity (0-4)
                                      pamRespectsDendro = FALSE, # Set to TRUE for PAM refinement
                                      minClusterSize = minModuleSize_diffcoex)
 ..cutHeight not given, setting it to 0.955  ===>  99% of the (truncated) height range in dendro.
 ..done.
# Assign module colors (for visualization and further analysis)
moduleColors_diffcoex <- labels2colors(dynamicMods_diffcoex)

cat("Module identification complete.\n")
Module identification complete.
cat("Number of differential co-expression modules found:", length(unique(moduleColors_diffcoex)) - 1,
    " (excluding grey for unassigned genes)\n") # -1 for 'grey' module
Number of differential co-expression modules found: 10  (excluding grey for unassigned genes)
cat("Sizes of modules:\n")
Sizes of modules:
print(table(moduleColors_diffcoex))
moduleColors_diffcoex
      black        blue       brown       green greenyellow     magenta        pink      purple         red 
         30          32          31          31          11          27          28          16          31 
  turquoise      yellow 
         32          31 
# Output the module assignments to a data frame
differential_modules <- data.frame(
  Gene = colnames(datExpr1),
  Module = moduleColors_diffcoex
)

# Save the dataframe to a CSV file
output_file <- "diffcoexp/moduleColors_synthetic_0.9.csv"
write.csv(differential_modules, file = output_file, row.names = F)
cat(paste0("\nModule assignments saved to ", output_file, "\n"))

Module assignments saved to diffcoexp/moduleColors_synthetic_0.9.csv
# --- New Section: Plotting Module-Trait Relationships ---

cat("\n--- Plotting Module-Trait Relationships ---\n")

--- Plotting Module-Trait Relationships ---
# Create a trait data frame for your samples
# The row names of the trait data frame must match the sample names in datExpr_combined
trait_data <- data.frame(
  Sample = c(rownames(datExpr1), rownames(datExpr2)),
  Condition = c(rep("Healthy", nrow(datExpr1)), rep("Diseased", nrow(datExpr2)))
)
rownames(trait_data) <- trait_data$Sample # Set sample names as row names

# Convert categorical trait to numeric for correlation (WGCNA expects numeric)
# 0 for Healthy, 1 for Diseased (arbitrary assignment)
trait_numeric <- as.data.frame(as.numeric(trait_data$Condition == "Diseased"))
rownames(trait_numeric) <- rownames(trait_data)
colnames(trait_numeric) <- "Diseased_vs_Healthy"

cat("Trait data prepared.\n")
Trait data prepared.
print(head(trait_numeric))
cat("\n")
# Calculate Module Eigengenes (MEs)
# MEs represent the "average" expression profile of each module.
# Use the combined expression data and the identified module colors.
MEs_diffcoex <- moduleEigengenes(datExpr_combined, moduleColors_diffcoex)$eigengenes
cat("Module Eigengenes calculated.\n")
Module Eigengenes calculated.
print(head(MEs_diffcoex))
cat("\n")
# Relate MEs to the trait (Condition)
# Calculate correlations and their p-values
moduleTraitCor <- WGCNA::cor(MEs_diffcoex, trait_numeric, use = "pairwise.complete.obs", method = "pearson") # Pearson is common for MEs
moduleTraitPvalue <- WGCNA::corPvalueStudent(moduleTraitCor, nrow(datExpr_combined)) # Number of samples

cat("Module-trait correlations and p-values calculated.\n\n")
Module-trait correlations and p-values calculated.
# Plotting the heatmap using labeledHeatmap
# Customize text matrix for correlation and p-value display
dev.off()
null device 
          1 
png("diffcoexp/DiffCoExp_Synthetic_0.9.png",width=6,height=5,units="in",res=600)

textMatrix = paste(signif(moduleTraitCor, 2), "\n(",
                    signif(moduleTraitPvalue, 1), ")", sep = "");
                    dim(textMatrix) = dim(moduleTraitCor)
                    par(mar = c(6, 10, 3, 3))
par(mar = c(8, 8, 2, 1))  # Reduce margins (bottom, left, top, right)
labeledHeatmap(Matrix = moduleTraitCor,
               xLabels = "Condition",
               yLabels = names(MEs_diffcoex),
               ySymbols = names(MEs_diffcoex),
               colorLabels = FALSE,
               colors = blueWhiteRed(50), # Red for positive, blue for negative correlation
               textMatrix = textMatrix,
               setStdMargins = FALSE,
               cex.text = 0.5, # Adjust text size
               zlim = c(-1, 1),
               main = paste("DiffCoEx Modules")
               )

cat("\nModule-Trait Relationship Heatmap generated.\n")

Module-Trait Relationship Heatmap generated.
dev.off()
null device 
          1 
exprs.h = read.csv("dcor-method/synthetic_healthy_0.9.txt", sep = "\t", header=T, row.names = "Genes")
exprs.d = read.csv("dcor-method/synthetic_diseased_0.9.txt", sep = "\t", header=T, row.names = "Genes")
allowWGCNAThreads()
Allowing multi-threading with up to 8 threads.
# Diseased - Healthy; high correlation --> diseased
res.high =diffcoexp(exprs.1 = exprs.d, exprs.2 = exprs.h,
                   r.method = "pearson",q.method = "bonferroni")
Finished running comparecor.
Finished running coexpr.
3039 gene pairs remain after half thresholding.
711 DCLs identified.
12 DCGs identified.

X. Precision & Recall

evaluate_method <- function(true_positive_genes, predicted_positive_modules, gene_module_df) {
  gene_module_df <- gene_module_df[!is.na(gene_module_df$gene) & !is.na(gene_module_df$module), ]
  all_genes <- unique(gene_module_df$gene)

  # Handle empty predicted modules
  if (length(predicted_positive_modules) == 0) {
    predicted_positive_genes <- character(0)
  } else {
    predicted_positive_genes <- gene_module_df$gene[gene_module_df$module %in% predicted_positive_modules]
  }

  TP <- sum(predicted_positive_genes %in% true_positive_genes)
  FP <- sum(!(predicted_positive_genes %in% true_positive_genes))
  FN <- sum(true_positive_genes %in% all_genes & !(true_positive_genes %in% predicted_positive_genes))
  TN <- sum(!(all_genes %in% true_positive_genes) & !(all_genes %in% predicted_positive_genes))

  precision <- ifelse((TP + FP) > 0, TP / (TP + FP), NA)
  recall <- ifelse((TP + FN) > 0, TP / (TP + FN), NA)

  return(data.frame(
    TP = TP, FP = FP, TN = TN, FN = FN,
    precision = precision,
    recall = recall
  ))
}

X.1 Low variability (p=0.1)

# Inputs
synthetic_data = read.csv("synthetic_expression_data_effect_0.1.csv", header=T)
true_pos_diseased_genes <- synthetic_data$X[151:300]

# Module mapping
gene_module_files <- list(
  WGCNA = "wgcna/moduleColors_WGCNA_0.1.csv",
  DiffCoExp = "diffcoexp/moduleColors_synthetic_0.1.csv",
  DGCA = "wgcna/moduleColors_WGCNA_0.1.csv",
  Our_Method = list("dcor-method/modules_diseased_0.1.csv",
                    "dcor-method/modules_healthy_0.1.csv")
  )

# Positively correlated modules
predicted_modules_by_method = list(
  WGCNA = character(0),
  DiffCoExp = character(0),
  DGCA= c("brown"),
  Our_Method =  list(c("turquoise", "red", "magenta", "brown", "yellow", "purple"),
                     c("yellow", "blue", "red", "brown")
  )
)

# Evaluation loop
results_list <- list()

for (method in names(gene_module_files)) {
  files <- gene_module_files[[method]]
  modules <- predicted_modules_by_method[[method]]
  
  # Combine all dataframes for this method
  combined_df <- data.frame()
  
  for (i in seq_along(files)) {
    df <- read.csv(files[[i]], stringsAsFactors = FALSE)
    colnames(df) <- c("gene", "module")

    # Append with file ID if needed (optional)
    combined_df <- rbind(combined_df, df)
  }

  # Remove duplicates (in case same gene appears multiple times)
  combined_df <- unique(combined_df)

  # Determine combined predicted modules
  if (is.character(modules)) {
    predicted_modules <- modules
  } else if (is.list(modules)) {
    predicted_modules <- unique(unlist(modules))
  } else {
    stop(paste("Invalid predicted modules format for method", method))
  }

  # Evaluate once for the combined gene-module mapping
  res <- evaluate_method(
    true_positive_genes = true_pos_diseased_genes,
    predicted_positive_modules = predicted_modules,
    gene_module_df = combined_df
  )

  results_list[[method]] <- data.frame(
    method = method,
    TP = res$TP,
    FP = res$FP,
    TN = res$TN,
    FN = res$FN,
    precision = res$precision*100,
    recall = res$recall*100,
    row.names = NULL
  )
}

# Combine all results into one summary data frame
summary_df <- do.call(rbind, results_list)
rownames(summary_df) <- summary_df$method
summary_df$method <- NULL

# View results
print(summary_df)

X.2 High variability (p=0.9)

# Inputs
synthetic_data = read.csv("synthetic_expression_data_effect_0.9.csv", header=T)
true_pos_diseased_genes <- synthetic_data$X[151:300]

# Module mapping
gene_module_files <- list(
  WGCNA = "wgcna/moduleColors_WGCNA_0.9.csv",
  DiffCoExp = "diffcoexp/moduleColors_synthetic_0.9.csv",
  DGCA = "wgcna/moduleColors_WGCNA_0.9.csv",
  Our_Method = list("dcor-method/modules_diseased_0.9.csv",
                    "dcor-method/modules_healthy_0.9.csv")
  )

# Positively correlated modules
predicted_modules_by_method = list(
  WGCNA = c("blue", "black", "red", "magenta", "greenyellow", "purple"),
  DiffCoExp = c("blue", "green", "greenyellow", "pink", "purple", "yellow"),
  DGCA = c("turquoise","greenyellow","magenta","black","pink"),
  Our_Method =  list(c("blue", "green", "pink", "brown"),
                     c("pink", "blue", "yellow", "black")
  )
)

# Evaluation loop
results_list <- list()

for (method in names(gene_module_files)) {
  files <- gene_module_files[[method]]
  modules <- predicted_modules_by_method[[method]]
  
  # Combine all dataframes for this method
  combined_df <- data.frame()
  
  for (i in seq_along(files)) {
    df <- read.csv(files[[i]], stringsAsFactors = FALSE)
    colnames(df) <- c("gene", "module")

    # Append with file ID if needed (optional)
    combined_df <- rbind(combined_df, df)
  }

  # Remove duplicates (in case same gene appears multiple times)
  combined_df <- unique(combined_df)

  # Determine combined predicted modules
  if (is.character(modules)) {
    predicted_modules <- modules
  } else if (is.list(modules)) {
    predicted_modules <- unique(unlist(modules))
  } else {
    stop(paste("Invalid predicted modules format for method", method))
  }

  # Evaluate once for the combined gene-module mapping
  res <- evaluate_method(
    true_positive_genes = true_pos_diseased_genes,
    predicted_positive_modules = predicted_modules,
    gene_module_df = combined_df
  )

  results_list[[method]] <- data.frame(
    method = method,
    TP = res$TP,
    FP = res$FP,
    TN = res$TN,
    FN = res$FN,
    precision = res$precision*100,
    recall = res$recall*100,
    row.names = NULL
  )
}

# Combine all results into one summary data frame
summary_df <- do.call(rbind, results_list)
rownames(summary_df) <- summary_df$method
summary_df$method <- NULL

# View results
print(summary_df)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBDb21wYXJpc29uIG9mIE1ldGhvZHMKCkhlcmUgd2Ugd2FudCB0byBkZXRlcm1pbmUgdGhlIHByZWNpc2lvbiBhbmQgcmVjYWxsIG9mIG91ciBtZXRob2RzIHVzaW5nIHRoZSBzeW50aGV0aWMgZGF0YXNldHMuCgojIEEuIFN5bnRoZXRpYyBkYXRhIHdpdGggbG93IGFuZCBoaWdoIHZhcmlhYmlsaXR5CgpgYGB7cn0KIyBpbnN0YWxsLnBhY2thZ2VzKCJNQVNTIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKIyBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCiMgaW5zdGFsbC5wYWNrYWdlcygidGlkeXIiKQojIGluc3RhbGwucGFja2FnZXMoInNmIikgIyBOZXcgZGVwZW5kZW5jeSBmb3IgZ2VvbWV0cmljIG9wZXJhdGlvbnMgKG92ZXJsYXAgY2FsY3VsYXRpb24pCgpsaWJyYXJ5KE1BU1MpICMgRm9yIG12cm5vcm0KbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KHNmKSAjIEZvciBzcGF0aWFsIGRhdGEgb3BlcmF0aW9ucyB0byBjYWxjdWxhdGUgZWxsaXBzZSBvdmVybGFwCgojIC0tLSAxLiBEZWZpbmUgRGF0YXNldCBQYXJhbWV0ZXJzIC0tLQpudW1fZ2VuZXMgPC0gMzAwICMgTiA9IDMwMCBnZW5lcwpzYW1wbGVzX3Blcl9jb25kaXRpb24gPC0gNTAgIyBNID0gNTAgc2FtcGxlcyBwZXIgY29uZGl0aW9uCm51bV9jb25kaXRpb25zIDwtIDIgIyBDb25kaXRpb25zIEhlYWx0aHkgYW5kIERpc2Vhc2VkCm51bV9tb2R1bGVzIDwtIDEwICMgTnVtYmVyIG9mIHByZWRlZmluZWQgZ2VuZSBtb2R1bGVzCgojIFNldCBhIHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCg0MikKCiMgLS0tIDIuIEFzc2lnbiBLbm93biBNb2R1bGUgTWVtYmVyc2hpcCB0byBHZW5lcyAtLS0KIyBFYWNoIGdlbmUgaXMgYXNzaWduZWQgdG8gb25lIG9mIHRoZSAnbnVtX21vZHVsZXMnLgojIEdlbmVzIGFyZSBkaXZpZGVkIGVxdWFsbHkgYW1vbmcgbW9kdWxlcy4KZ2VuZXNfcGVyX21vZHVsZSA8LSBudW1fZ2VuZXMgJS8lIG51bV9tb2R1bGVzICMgSW50ZWdlciBkaXZpc2lvbgpnZW5lX2lkcyA8LSBwYXN0ZTAoIkdlbmVfIiwgMTpudW1fZ2VuZXMpCgojIENyZWF0ZSBtb2R1bGUgYXNzaWdubWVudHMgKGUuZy4sIEdlbmVfMDAxIHRvIEdlbmVfMDMwIGFyZSBNb2R1bGVfMSwgZXRjLikKbW9kdWxlX251bXMgPC0gKCgwOihudW1fZ2VuZXMtMSkpICUvJSBnZW5lc19wZXJfbW9kdWxlKSArIDEKZ2VuZV9tb2R1bGVfbWVtYmVyc2hpcCA8LSBkYXRhLmZyYW1lKAogICAgR2VuZSA9IGdlbmVfaWRzLAogICAgTW9kdWxlID0gcGFzdGUwKCJNb2R1bGVfIiwgbW9kdWxlX251bXMpCikKCmNhdCgiLS0tIEdlbmUgTW9kdWxlIE1lbWJlcnNoaXAgKGZpcnN0IDEwIGdlbmVzKSAtLS1cbiIpCnByaW50KGhlYWQoZ2VuZV9tb2R1bGVfbWVtYmVyc2hpcCwgMTApKQpjYXQocGFzdGUwKHJlcCgiLSIsIDUwKSwgY29sbGFwc2UgPSAiIiksICJcblxuIikKCiMgLS0tIDMuIERlZmluZSBQYXJhbWV0ZXJzIGZvciBNZWFuIEV4cHJlc3Npb24gUHJvZmlsZXMgYW5kIE92ZXJsYXAgb24gTE9HIFNDQUxFIC0tLQojIFRoZXNlIHBhcmFtZXRlcnMgbm93IHJlZmVyIHRvIHRoZSBsb2ctdHJhbnNmb3JtZWQgZ2VuZSBleHByZXNzaW9uIHZhbHVlcy4KbG9nX2Jhc2VsaW5lX21lYW4gPC0gbG9nKDEwLjApICMgQmFzZSBtZWFuIGV4cHJlc3Npb24gbGV2ZWwgb24gbG9nIHNjYWxlIChlLmcuLCBsb2coMTApID0gMi4zKQoKIyBNYWduaXR1ZGUgb2YgdGhlIG1vZHVsZS1zcGVjaWZpYyBlZmZlY3Qgb24gdGhlIExPRyBzY2FsZS4KIyBBIHNtYWxsZXIgdmFsdWUgaGVyZSBtZWFucyBsZXNzIHNlcGFyYXRpb24gb24gdGhlIGxvZyBzY2FsZSwgbGVhZGluZyB0byBtb3JlIG92ZXJsYXAKIyBpbiB0aGUgUENBIHNwYWNlIG9mIHRoZSBmaW5hbCwgZXhwb25lbnRpYXRlZCBkYXRhLgpsb2dfbW9kdWxlX2VmZmVjdF9tYWduaXR1ZGUgPC0gMC45ICMgVGhpcyB2YWx1ZSBpcyBoaWdoLCBleHBlY3RpbmcgbG93IG92ZXJsYXAuCiMgbG9nX21vZHVsZV9lZmZlY3RfbWFnbml0dWRlIDwtIDAuMSAjIEhpZ2ggb3ZlcmxhcAoKIyBOZXc6IFBhcmFtZXRlciBmb3Igbm9ubGluZWFyIChjdWJpYykgY29ycmVsYXRpb24gc3RyZW5ndGgKIyBUaGlzIGNvbnRyb2xzIHRoZSBtYWduaXR1ZGUgb2YgdGhlIHNoYXJlZCBjdWJpYyBlZmZlY3Qgd2l0aGluIG1vZHVsZXMuCiMgQSBzbWFsbCB2YWx1ZSBpcyByZWNvbW1lbmRlZCBhcyBjdWJpYyB0ZXJtcyBncm93IHJhcGlkbHkuCm5vbmxpbmVhcl9jb3JyZWxhdGlvbl9zdHJlbmd0aCA8LSAwLjEgIyBBZGp1c3QgdGhpcyB2YWx1ZSB0byBjb250cm9sIHRoZSBjdWJpYyBlZmZlY3QncyBtYWduaXR1ZGUKCiMgLS0tIDQuIENvbnN0cnVjdCBDb3ZhcmlhbmNlIE1hdHJpeCBmb3IgV2l0aGluLU1vZHVsZSBDby1leHByZXNzaW9uIG9uIExPRyBTQ0FMRSAtLS0KIyBUaGlzIG1hdHJpeCB3aWxsIGRlZmluZSB0aGUgbGluZWFyIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSBiZXR3ZWVuIGdlbmVzIG9uIHRoZSBsb2cgc2NhbGUuCiMgR2VuZXMgd2l0aGluIHRoZSBzYW1lIG1vZHVsZSB3aWxsIGJlIGxpbmVhcmx5IGNvcnJlbGF0ZWQ7IGdlbmVzIGluIGRpZmZlcmVudCBtb2R1bGVzIHdpbGwgbm90LgojIFRoZSBjdWJpYyB0ZXJtIHdpbGwgYWRkIGFuIGFkZGl0aW9uYWwsIG5vbi1saW5lYXIgY29ycmVsYXRpb24gb24gdG9wIG9mIHRoaXMuCgojIENvbnRyb2xzIHRoZSBiYXNlIHZhcmlhbmNlIG9mIGVhY2ggZ2VuZSBvbiB0aGUgbG9nIHNjYWxlCmxvZ19nZW5lX3ZhcmlhbmNlIDwtIDAuMyAjIFJlZHVjZWQgdG8gYXZvaWQgZXhjZXNzaXZlbHkgd2lkZSB0YWlscyBhZnRlciBleHBvbmVudGlhdGlvbgoKIyBDb250cm9scyB0aGUgbGluZWFyIGNvcnJlbGF0aW9uIHN0cmVuZ3RoIHdpdGhpbiBhIG1vZHVsZSAoMCB0byAxKSBvbiB0aGUgbG9nIHNjYWxlCndpdGhpbl9tb2R1bGVfY29ycmVsYXRpb24gPC0gMC41CgojIEluaXRpYWxpemUgYSBkaWFnb25hbCBjb3ZhcmlhbmNlIG1hdHJpeCAoZ2VuZXMgYXJlIGluaXRpYWxseSBpbmRlcGVuZGVudCkKY292YXJpYW5jZV9tYXRyaXggPC0gZGlhZyhudW1fZ2VuZXMpICogbG9nX2dlbmVfdmFyaWFuY2UKY29sbmFtZXMoY292YXJpYW5jZV9tYXRyaXgpIDwtIGdlbmVfaWRzCnJvd25hbWVzKGNvdmFyaWFuY2VfbWF0cml4KSA8LSBnZW5lX2lkcwoKIyBQb3B1bGF0ZSB0aGUgY292YXJpYW5jZSBtYXRyaXggZm9yIHdpdGhpbi1tb2R1bGUgbGluZWFyIGNvLWV4cHJlc3Npb24KZm9yIChpIGluIDE6bnVtX2dlbmVzKSB7CiAgICBmb3IgKGogaW4gaTpudW1fZ2VuZXMpIHsgIyBJdGVyYXRlIHRocm91Z2ggdXBwZXIgdHJpYW5nbGUgKG1hdHJpeCBpcyBzeW1tZXRyaWMpCiAgICAgICAgaWYgKGkgPT0gaikgewogICAgICAgICAgICAjIERpYWdvbmFsIGVsZW1lbnRzIGFyZSB2YXJpYW5jZXMgKGFscmVhZHkgc2V0KQogICAgICAgICAgICBuZXh0CiAgICAgICAgfQogICAgICAgIAogICAgICAgIGdlbmUxX21vZHVsZSA8LSBnZW5lX21vZHVsZV9tZW1iZXJzaGlwJE1vZHVsZVtnZW5lX21vZHVsZV9tZW1iZXJzaGlwJEdlbmUgPT0gZ2VuZV9pZHNbaV1dCiAgICAgICAgZ2VuZTJfbW9kdWxlIDwtIGdlbmVfbW9kdWxlX21lbWJlcnNoaXAkTW9kdWxlW2dlbmVfbW9kdWxlX21lbWJlcnNoaXAkR2VuZSA9PSBnZW5lX2lkc1tqXV0KICAgICAgICAKICAgICAgICBpZiAoZ2VuZTFfbW9kdWxlID09IGdlbmUyX21vZHVsZSkgewogICAgICAgICAgICAjIElmIGdlbmVzIGFyZSBpbiB0aGUgc2FtZSBtb2R1bGUsIHNldCB0aGVpciBjb3ZhcmlhbmNlIGJhc2VkIG9uIGxpbmVhciBjb3JyZWxhdGlvbgogICAgICAgICAgICAjIENvdihYLFkpID0gQ29ycihYLFkpICogU0QoWCkgKiBTRChZKQogICAgICAgICAgICAjIEFzc3VtaW5nIFNEKFgpID0gU0QoWSkgPSBzcXJ0KGxvZ19nZW5lX3ZhcmlhbmNlKQogICAgICAgICAgICBjb3ZhcmlhbmNlX3ZhbHVlIDwtIHdpdGhpbl9tb2R1bGVfY29ycmVsYXRpb24gKiBsb2dfZ2VuZV92YXJpYW5jZQogICAgICAgICAgICBjb3ZhcmlhbmNlX21hdHJpeFtpLCBqXSA8LSBjb3ZhcmlhbmNlX3ZhbHVlCiAgICAgICAgICAgIGNvdmFyaWFuY2VfbWF0cml4W2osIGldIDwtIGNvdmFyaWFuY2VfdmFsdWUgIyBTeW1tZXRyaWMKICAgICAgICB9CiAgICB9Cn0KCmNhdCgiLS0tIFNhbXBsZSBvZiBDb3ZhcmlhbmNlIE1hdHJpeCAoZmlyc3QgNXg1IGJsb2NrLCBvbiBsb2cgc2NhbGUpIC0tLVxuIikKcHJpbnQocm91bmQoY292YXJpYW5jZV9tYXRyaXhbMTo1LCAxOjVdLCAyKSkKY2F0KHBhc3RlMChyZXAoIi0iLCA1MCksIGNvbGxhcHNlID0gIiIpLCAiXG5cbiIpCgoKIyAtLS0gNS4gRGVmaW5lIE1lYW4gRXhwcmVzc2lvbiBQcm9maWxlcyBmb3IgSGVhbHRoeSBhbmQgRGlzZWFzZWQgKE1vZHVsZS1hd2FyZSwgb24gTE9HIFNDQUxFKSAtLS0KbWVhbl9wcm9maWxlX0hlYWx0aHlfbG9nIDwtIHJlcChsb2dfYmFzZWxpbmVfbWVhbiwgbnVtX2dlbmVzKQptZWFuX3Byb2ZpbGVfRGlzZWFzZWRfbG9nIDwtIHJlcChsb2dfYmFzZWxpbmVfbWVhbiwgbnVtX2dlbmVzKQoKZm9yIChpIGluIDE6bnVtX2dlbmVzKSB7CiAgICBnZW5lX2lkIDwtIGdlbmVfaWRzW2ldCiAgICBtb2R1bGVfaWRfc3RyIDwtIGdlbmVfbW9kdWxlX21lbWJlcnNoaXAkTW9kdWxlW2dlbmVfbW9kdWxlX21lbWJlcnNoaXAkR2VuZSA9PSBnZW5lX2lkXQogICAgbW9kdWxlX251bSA8LSBhcy5udW1lcmljKHN0cnNwbGl0KG1vZHVsZV9pZF9zdHIsICJfIilbWzFdXVsyXSkKCiAgICAjIERpZmZlcmVudGlhbCBleHByZXNzaW9uIHN0cmF0ZWd5OgogICAgIyBNb2R1bGVzIDEgdG8gKG51bV9tb2R1bGVzLzIpIHdpbGwgaGF2ZSBoaWdoZXIgZXhwcmVzc2lvbiBpbiBDb25kaXRpb24gSGVhbHRoeQogICAgIyBNb2R1bGVzIChudW1fbW9kdWxlcy8yICsgMSkgdG8gbnVtX21vZHVsZXMgd2lsbCBoYXZlIGhpZ2hlciBleHByZXNzaW9uIGluIENvbmRpdGlvbiBEaXNlYXNlZAogICAgaWYgKG1vZHVsZV9udW0gPD0gbnVtX21vZHVsZXMgLyAyKSB7CiAgICAgICAgbWVhbl9wcm9maWxlX0hlYWx0aHlfbG9nW2ldIDwtIG1lYW5fcHJvZmlsZV9IZWFsdGh5X2xvZ1tpXSArIGxvZ19tb2R1bGVfZWZmZWN0X21hZ25pdHVkZQogICAgfSBlbHNlIHsKICAgICAgICBtZWFuX3Byb2ZpbGVfRGlzZWFzZWRfbG9nW2ldIDwtIG1lYW5fcHJvZmlsZV9EaXNlYXNlZF9sb2dbaV0gKyBsb2dfbW9kdWxlX2VmZmVjdF9tYWduaXR1ZGUKICAgIH0KfQoKY2F0KCItLS0gU2FtcGxlIG9mIE1lYW4gRXhwcmVzc2lvbiBQcm9maWxlcyAoZmlyc3QgMTAgZ2VuZXMsIG9uIGxvZyBzY2FsZSkgLS0tXG4iKQpwcmludChkYXRhLmZyYW1lKEdlbmUgPSBnZW5lX2lkc1sxOjEwXSwgTWVhbl9IZWFsdGh5X2xvZyA9IG1lYW5fcHJvZmlsZV9IZWFsdGh5X2xvZ1sxOjEwXSwgTWVhbl9EaXNlYXNlZF9sb2cgPSBtZWFuX3Byb2ZpbGVfRGlzZWFzZWRfbG9nWzE6MTBdKSkKY2F0KHBhc3RlMChyZXAoIi0iLCA1MCksIGNvbGxhcHNlID0gIiIpLCAiXG5cbiIpCgoKIyAtLS0gNi4gR2VuZXJhdGUgU3ludGhldGljIEdlbmUgRXhwcmVzc2lvbiBEYXRhIChmcm9tIExvZy1Ob3JtYWwgRGlzdHJpYnV0aW9uIHdpdGggQ3ViaWMgVGVybSkgLS0tCgojIEdlbmVyYXRlIG1vZHVsZS1zcGVjaWZpYyBsYXRlbnQgdmFyaWFibGVzIGZvciBjdWJpYyBlZmZlY3QKIyBUaGVzZSB3aWxsIGJlIHNoYXJlZCBieSBhbGwgZ2VuZXMgd2l0aGluIGEgbW9kdWxlIGZvciBhIGdpdmVuIHNhbXBsZSwgYW5kIGFyZSBpbmRlcGVuZGVudCBvZiBtdnJub3JtIG5vaXNlCm1vZHVsZV9sYXRlbnRfdmFyc19oZWFsdGh5IDwtIG1hdHJpeChybm9ybShudW1fbW9kdWxlcyAqIHNhbXBsZXNfcGVyX2NvbmRpdGlvbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gbnVtX21vZHVsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gc2FtcGxlc19wZXJfY29uZGl0aW9uKQptb2R1bGVfbGF0ZW50X3ZhcnNfZGlzZWFzZWQgPC0gbWF0cml4KHJub3JtKG51bV9tb2R1bGVzICogc2FtcGxlc19wZXJfY29uZGl0aW9uKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBucm93ID0gbnVtX21vZHVsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IHNhbXBsZXNfcGVyX2NvbmRpdGlvbikKCgojIEdlbmVyYXRlIGJhc2UgZGF0YSBmb3IgSGVhbHRoeSBzYW1wbGVzIChsaW5lYXJseSBjb3JyZWxhdGVkIG9uIGxvZy1zY2FsZSkKZXhwcmVzc2lvbl9IZWFsdGh5X2xvZ19iYXNlIDwtIHQoTUFTUzo6bXZybm9ybShuID0gc2FtcGxlc19wZXJfY29uZGl0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG11ID0gbWVhbl9wcm9maWxlX0hlYWx0aHlfbG9nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNpZ21hID0gY292YXJpYW5jZV9tYXRyaXgpKQoKIyBBZGQgdGhlIGN1YmljIHRlcm0gdG8gaW50cm9kdWNlIG5vbi1saW5lYXIgY29ycmVsYXRpb24gd2l0aGluIG1vZHVsZXMKZXhwcmVzc2lvbl9IZWFsdGh5X2xvZyA8LSBleHByZXNzaW9uX0hlYWx0aHlfbG9nX2Jhc2UKZm9yIChzIGluIDE6c2FtcGxlc19wZXJfY29uZGl0aW9uKSB7CiAgICBmb3IgKGdfaWR4IGluIDE6bnVtX2dlbmVzKSB7CiAgICAgICAgbW9kdWxlX251bSA8LSBhcy5udW1lcmljKHN0cnNwbGl0KGdlbmVfbW9kdWxlX21lbWJlcnNoaXAkTW9kdWxlW2dfaWR4XSwgIl8iKVtbMV1dWzJdKQogICAgICAgICMgQWRkIHRoZSBzY2FsZWQgY3ViaWMgdGVybQogICAgICAgIGN1YmljX3Rlcm0gPC0gbm9ubGluZWFyX2NvcnJlbGF0aW9uX3N0cmVuZ3RoICogKG1vZHVsZV9sYXRlbnRfdmFyc19oZWFsdGh5W21vZHVsZV9udW0sIHNdKV4zCiAgICAgICAgZXhwcmVzc2lvbl9IZWFsdGh5X2xvZ1tnX2lkeCwgc10gPC0gZXhwcmVzc2lvbl9IZWFsdGh5X2xvZ1tnX2lkeCwgc10gKyBjdWJpY190ZXJtCiAgICB9Cn0KZXhwcmVzc2lvbl9IZWFsdGh5IDwtIGV4cChleHByZXNzaW9uX0hlYWx0aHlfbG9nKSAjIENvbnZlcnQgZnJvbSBsb2cgdG8gbGluZWFyIHNjYWxlCmNvbG5hbWVzKGV4cHJlc3Npb25fSGVhbHRoeSkgPC0gcGFzdGUwKCJIZWFsdGh5X1NhbXBsZV8iLCAxOnNhbXBsZXNfcGVyX2NvbmRpdGlvbikKcm93bmFtZXMoZXhwcmVzc2lvbl9IZWFsdGh5KSA8LSBwYXN0ZTAoIkdlbmVfIiwgMTpudW1fZ2VuZXMpCgoKIyBHZW5lcmF0ZSBiYXNlIGRhdGEgZm9yIERpc2Vhc2VkIHNhbXBsZXMgKGxpbmVhcmx5IGNvcnJlbGF0ZWQgb24gbG9nLXNjYWxlKQpleHByZXNzaW9uX0Rpc2Vhc2VkX2xvZ19iYXNlIDwtIHQoTUFTUzo6bXZybm9ybShuID0gc2FtcGxlc19wZXJfY29uZGl0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdSA9IG1lYW5fcHJvZmlsZV9EaXNlYXNlZF9sb2csCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNpZ21hID0gY292YXJpYW5jZV9tYXRyaXgpKQoKIyBBZGQgdGhlIGN1YmljIHRlcm0gdG8gaW50cm9kdWNlIG5vbi1saW5lYXIgY29ycmVsYXRpb24gd2l0aGluIG1vZHVsZXMKZXhwcmVzc2lvbl9EaXNlYXNlZF9sb2cgPC0gZXhwcmVzc2lvbl9EaXNlYXNlZF9sb2dfYmFzZQpmb3IgKHMgaW4gMTpzYW1wbGVzX3Blcl9jb25kaXRpb24pIHsKICAgIGZvciAoZ19pZHggaW4gMTpudW1fZ2VuZXMpIHsKICAgICAgICBtb2R1bGVfbnVtIDwtIGFzLm51bWVyaWMoc3Ryc3BsaXQoZ2VuZV9tb2R1bGVfbWVtYmVyc2hpcCRNb2R1bGVbZ19pZHhdLCAiXyIpW1sxXV1bMl0pCiAgICAgICAgIyBBZGQgdGhlIHNjYWxlZCBjdWJpYyB0ZXJtCiAgICAgICAgY3ViaWNfdGVybSA8LSBub25saW5lYXJfY29ycmVsYXRpb25fc3RyZW5ndGggKiAobW9kdWxlX2xhdGVudF92YXJzX2Rpc2Vhc2VkW21vZHVsZV9udW0sIHNdKV4zCiAgICAgICAgZXhwcmVzc2lvbl9EaXNlYXNlZF9sb2dbZ19pZHgsIHNdIDwtIGV4cHJlc3Npb25fRGlzZWFzZWRfbG9nW2dfaWR4LCBzXSArIGN1YmljX3Rlcm0KICAgIH0KfQpleHByZXNzaW9uX0Rpc2Vhc2VkIDwtIGV4cChleHByZXNzaW9uX0Rpc2Vhc2VkX2xvZykgIyBDb252ZXJ0IGZyb20gbG9nIHRvIGxpbmVhciBzY2FsZQpjb2xuYW1lcyhleHByZXNzaW9uX0Rpc2Vhc2VkKSA8LSBwYXN0ZTAoIkRpc2Vhc2VkX1NhbXBsZV8iLCAxOnNhbXBsZXNfcGVyX2NvbmRpdGlvbikKcm93bmFtZXMoZXhwcmVzc2lvbl9EaXNlYXNlZCkgPC0gcGFzdGUwKCJHZW5lXyIsIDE6bnVtX2dlbmVzKQoKIyBFbnN1cmUgYWxsIGV4cHJlc3Npb24gdmFsdWVzIGFyZSBub24tbmVnYXRpdmUgKGNsaXAgYXQgYSBzbWFsbCB2YWx1ZSB0byBhdm9pZCBleGFjdCB6ZXJvKQpleHByZXNzaW9uX0hlYWx0aHlbZXhwcmVzc2lvbl9IZWFsdGh5IDwgMC4wMV0gPC0gMC4wMQpleHByZXNzaW9uX0Rpc2Vhc2VkW2V4cHJlc3Npb25fRGlzZWFzZWQgPCAwLjAxXSA8LSAwLjAxCgoKIyBDb21iaW5lIGludG8gYSBzaW5nbGUgZ2VuZSBleHByZXNzaW9uIG1hdHJpeApzeW50aGV0aWNfZXhwcmVzc2lvbl9kYXRhIDwtIGNiaW5kKGV4cHJlc3Npb25fSGVhbHRoeSwgZXhwcmVzc2lvbl9EaXNlYXNlZCkKCgojIC0tLSA3LiBDcmVhdGUgU2FtcGxlIE1ldGFkYXRhIChQaGVub3R5cGUvQ29uZGl0aW9uIExhYmVscykgLS0tCnNhbXBsZV9tZXRhZGF0YSA8LSBkYXRhLmZyYW1lKAogIFNhbXBsZV9JRCA9IGNvbG5hbWVzKHN5bnRoZXRpY19leHByZXNzaW9uX2RhdGEpLAogIENvbmRpdGlvbiA9IGMocmVwKCJIZWFsdGh5Iiwgc2FtcGxlc19wZXJfY29uZGl0aW9uKSwgcmVwKCJEaXNlYXNlZCIsIHNhbXBsZXNfcGVyX2NvbmRpdGlvbikpCikKcm93bmFtZXMoc2FtcGxlX21ldGFkYXRhKSA8LSBzYW1wbGVfbWV0YWRhdGEkU2FtcGxlX0lECgpwcmludCgiLS0tIFN5bnRoZXRpYyBEYXRhc2V0IENyZWF0ZWQgLS0tIikKcHJpbnQocGFzdGUoIkRpbWVuc2lvbnMgb2YgZXhwcmVzc2lvbiBkYXRhIChnZW5lcyB4IHNhbXBsZXMpOiIsCiAgICAgICAgICAgIGRpbShzeW50aGV0aWNfZXhwcmVzc2lvbl9kYXRhKVsxXSwgIngiLCBkaW0oc3ludGhldGljX2V4cHJlc3Npb25fZGF0YSlbMl0pKQpwcmludCgiRmlyc3QgNSByb3dzIGFuZCA1IGNvbHVtbnMgb2YgZXhwcmVzc2lvbiBkYXRhIChsaW5lYXIgc2NhbGUpOiIpCnByaW50KGhlYWQoc3ludGhldGljX2V4cHJlc3Npb25fZGF0YVssIDE6NV0pKQpwcmludCgiRmlyc3QgNSByb3dzIG9mIHNhbXBsZSBtZXRhZGF0YToiKQpwcmludChoZWFkKHNhbXBsZV9tZXRhZGF0YSkpCmNhdChwYXN0ZTAocmVwKCItIiwgNTApLCBjb2xsYXBzZSA9ICIiKSwgIlxuXG4iKQoKIyAtLS0gU2F2ZSBzeW50aGV0aWNfZXhwcmVzc2lvbl9kYXRhLCBzYW1wbGVfbWV0YWRhdGEsIGFuZCBnZW5lX21vZHVsZV9tZW1iZXJzaGlwIC0tLQojIENvbnN0cnVjdCBmaWxlbmFtZXMgdG8gaW5jbHVkZSBtb2R1bGVfZWZmZWN0X21hZ25pdHVkZQpleHByZXNzaW9uX2ZpbGVuYW1lIDwtIHBhc3RlMCgic3ludGhldGljX2V4cHJlc3Npb25fZGF0YV9lZmZlY3RfIiwgbG9nX21vZHVsZV9lZmZlY3RfbWFnbml0dWRlLCAiLmNzdiIpCm1ldGFkYXRhX2ZpbGVuYW1lIDwtIHBhc3RlMCgic2FtcGxlX21ldGFkYXRhX2VmZmVjdF8iLCBsb2dfbW9kdWxlX2VmZmVjdF9tYWduaXR1ZGUsICIuY3N2IikKbW9kdWxlX21lbWJlcnNoaXBfZmlsZW5hbWUgPC0gcGFzdGUwKCJnZW5lX21vZHVsZV9tZW1iZXJzaGlwX2VmZmVjdF8iLCBsb2dfbW9kdWxlX2VmZmVjdF9tYWduaXR1ZGUsICIuY3N2IikKCgp3cml0ZS5jc3Yoc3ludGhldGljX2V4cHJlc3Npb25fZGF0YSwgZXhwcmVzc2lvbl9maWxlbmFtZSwgcm93Lm5hbWVzID0gVFJVRSkKY2F0KHNwcmludGYoIlNhdmVkICVzXG4iLCBleHByZXNzaW9uX2ZpbGVuYW1lKSkKd3JpdGUuY3N2KHNhbXBsZV9tZXRhZGF0YSwgbWV0YWRhdGFfZmlsZW5hbWUsIHJvdy5uYW1lcyA9IEZBTFNFKQpjYXQoc3ByaW50ZigiU2F2ZWQgJXNcbiIsIG1ldGFkYXRhX2ZpbGVuYW1lKSkKd3JpdGUuY3N2KGdlbmVfbW9kdWxlX21lbWJlcnNoaXAsIG1vZHVsZV9tZW1iZXJzaGlwX2ZpbGVuYW1lLCByb3cubmFtZXMgPSBGQUxTRSkKY2F0KHNwcmludGYoIlNhdmVkICVzXG5cbiIsIG1vZHVsZV9tZW1iZXJzaGlwX2ZpbGVuYW1lKSkKCgojIC0tLSA4LiBWZXJpZnkgT3ZlcmxhcCB3aXRoIFBDQSBQbG90IC0tLQoKIyBUcmFuc3Bvc2UgdGhlIGV4cHJlc3Npb24gZGF0YSBmb3IgUENBIChQQ0EgZXhwZWN0cyBzYW1wbGVzIGFzIHJvd3MsIGdlbmVzIGFzIGNvbHVtbnMpCnBjYV9pbnB1dF9kYXRhIDwtIHQoc3ludGhldGljX2V4cHJlc3Npb25fZGF0YSkKCiMgUGVyZm9ybSBQQ0EKcGNhX3Jlc3VsdHMgPC0gcHJjb21wKHBjYV9pbnB1dF9kYXRhLCBjZW50ZXIgPSBUUlVFLCBzY2FsZS4gPSBUUlVFKSAjIFNjYWxlIGlzIHVzdWFsbHkgZ29vZCBmb3IgZ2VuZSBleHByZXNzaW9uIFBDQQoKIyBFeHRyYWN0IFBDQSBzY29yZXMgKGNvb3JkaW5hdGVzIG9mIHNhbXBsZXMgaW4gUENBIHNwYWNlKQpwY2Ffc2NvcmVzIDwtIGFzLmRhdGEuZnJhbWUocGNhX3Jlc3VsdHMkeCkKCiMgQWRkIGNvbmRpdGlvbiBsYWJlbHMgdG8gdGhlIFBDQSBzY29yZXMKcGNhX3Njb3JlcyRDb25kaXRpb24gPC0gc2FtcGxlX21ldGFkYXRhW3Jvd25hbWVzKHBjYV9zY29yZXMpLCAiQ29uZGl0aW9uIl0KCiMgQ2FsY3VsYXRlIGV4cGxhaW5lZCB2YXJpYW5jZSBmb3IgUEMxIGFuZCBQQzIKcGNhX3N1bW1hcnkgPC0gc3VtbWFyeShwY2FfcmVzdWx0cykKcGMxX3Zhcl9leHBsYWluZWQgPC0gcm91bmQocGNhX3N1bW1hcnkkaW1wb3J0YW5jZVsyLCAxXSAqIDEwMCwgMikKcGMyX3Zhcl9leHBsYWluZWQgPC0gcm91bmQocGNhX3N1bW1hcnkkaW1wb3J0YW5jZVsyLCAyXSAqIDEwMCwgMikKCiMgLS0tIEZ1bmN0aW9uIHRvIGdlbmVyYXRlIGNsb3NlZCBlbGxpcHNlIHBvaW50cyBmb3Igb3ZlcmxhcCBjYWxjdWxhdGlvbiAtLS0KZWxsaXBzZV9wb2ludHMgPC0gZnVuY3Rpb24oZGYsIGxldmVsID0gMC45NSwgbiA9IDEwMCkgewogICMgRm9yIGNvbmZpZGVuY2UgZWxsaXBzZXMsIHRoZSBzY2FsaW5nIGZhY3RvciBmcm9tIHN0YXRzOjplbGxpcHNlIGlzIGNoaXNxKGxldmVsKS8yCiAgIyBGb3Igbm9ybWFsIGRpc3RyaWJ1dGlvbiwgaXQncyBzcXJ0KHFjaGlzcShsZXZlbCwgZGYgPSAyKSkKICAjIFRoZSBzdGF0X2VsbGlwc2UgdXNlcyBhIHQtZGlzdHJpYnV0aW9uIGJ5IGRlZmF1bHQsIG9yIG5vcm1hbCBpZiB0eXBlPSJub3JtIgogICMgSGVyZSB3ZSB1c2UgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBmb3Igc2NhbGluZyB0byBtYXRjaCBhIHNwZWNpZmljIGNvbmZpZGVuY2UgcmVnaW9uCiAgCiAgIyBDYWxjdWxhdGUgOTUlIGNvbmZpZGVuY2UgcmVnaW9uIHNjYWxpbmcgZmFjdG9yIGZyb20gYSBjaGktc3F1YXJlZCBkaXN0cmlidXRpb24gd2l0aCAyIGRlZ3JlZXMgb2YgZnJlZWRvbQogICMgKHNpbmNlIHdlIGFyZSBpbiAyRCBmb3IgUEMxIGFuZCBQQzIpCiAgc2NhbGVfZmFjdG9yIDwtIHNxcnQocWNoaXNxKGxldmVsLCBkZiA9IDIpKQogIAogIGNvdl9tYXQgPC0gY292KGRmWywgYygiUEMxIiwgIlBDMiIpXSkKICBtZWFuX3ZhbHMgPC0gY29sTWVhbnMoZGZbLCBjKCJQQzEiLCAiUEMyIildKQogIAogIGFuZ2xlcyA8LSBzZXEoMCwgMiAqIHBpLCBsZW5ndGgub3V0ID0gbikKICAKICAjIFNjYWxlIGJ5IHRoZSBzcXVhcmUgcm9vdCBvZiB0aGUgZWlnZW52YWx1ZXMgYW5kIHJvdGF0ZSBieSBlaWdlbnZlY3RvcnMgKGNob2xlc2t5IGRlY29tcG9zaXRpb24pCiAgZWxsaXBzZV9jb29yZHMgPC0gdChjaG9sKGNvdl9tYXQpKSAlKiUgcmJpbmQoY29zKGFuZ2xlcyksIHNpbihhbmdsZXMpKQogIAogIGNvb3JkcyA8LSBkYXRhLmZyYW1lKAogICAgUEMxID0gbWVhbl92YWxzWzFdICsgc2NhbGVfZmFjdG9yICogZWxsaXBzZV9jb29yZHNbMSxdLAogICAgUEMyID0gbWVhbl92YWxzWzJdICsgc2NhbGVfZmFjdG9yICogZWxsaXBzZV9jb29yZHNbMixdCiAgKQogIAogICMgQ2xvc2UgdGhlIHBvbHlnb24gYnkgYWRkaW5nIHRoZSBmaXJzdCBwb2ludCBhdCB0aGUgZW5kCiAgY29vcmRzIDwtIHJiaW5kKGNvb3JkcywgY29vcmRzWzEsXSkKICByZXR1cm4oY29vcmRzKQp9CgojIC0tLSBDb21wdXRlIE92ZXJsYXAgb2YgRWxsaXBzZXMgLS0tCiMgVXNlIHRoZSBwY2Ffc2NvcmVzIGFzIHRoZSBwY2FfZGYgbWVudGlvbmVkIGluIHRoZSB1c2VyJ3Mgc25pcHBldApwY2FfZGZfZm9yX292ZXJsYXAgPC0gcGNhX3Njb3JlcwoKIyBHZW5lcmF0ZSBjbG9zZWQgZWxsaXBzZSBwb2ludHMgZm9yIGVhY2ggY29uZGl0aW9uCmVsbGlwc2VfaGVhbHRoeSA8LSBlbGxpcHNlX3BvaW50cyhwY2FfZGZfZm9yX292ZXJsYXAgJT4lIGZpbHRlcihDb25kaXRpb24gPT0gIkhlYWx0aHkiKSkKZWxsaXBzZV9kaXNlYXNlZCA8LSBlbGxpcHNlX3BvaW50cyhwY2FfZGZfZm9yX292ZXJsYXAgJT4lIGZpbHRlcihDb25kaXRpb24gPT0gIkRpc2Vhc2VkIikpCgojIENvbnZlcnQgdG8gc2YgcG9seWdvbnMKIyBOZWVkIHRvIHNldCBhIENSUywgNDMyNiBpcyBXR1M4NCwgY29tbW9uIGZvciBnZW9ncmFwaGljIGNvb3JkcywgYnV0IGNhbiBiZSBhcmJpdHJhcnkgZm9yIGFic3RyYWN0IHNwYWNlCnBvbHlfaGVhbHRoeSA8LSBzdF9wb2x5Z29uKGxpc3QoYXMubWF0cml4KGVsbGlwc2VfaGVhbHRoeSkpKQpwb2x5X2Rpc2Vhc2VkIDwtIHN0X3BvbHlnb24obGlzdChhcy5tYXRyaXgoZWxsaXBzZV9kaXNlYXNlZCkpKQoKIyBTZXQgQ1JTIHRvIGVuc3VyZSBzdF9pbnRlcnNlY3Rpb24gd29ya3MgKGNhbiBiZSBhbnkgdmFsaWQgQ1JTIGZvciBhcmJpdHJhcnkgY29vcmRpbmF0ZXMpCnBvbHlfaGVhbHRoeV9zZmMgPC0gc3Rfc2ZjKHBvbHlfaGVhbHRoeSwgY3JzID0gNDMyNikKcG9seV9kaXNlYXNlZF9zZmMgPC0gc3Rfc2ZjKHBvbHlfZGlzZWFzZWQsIGNycyA9IDQzMjYpCgojIENvbXB1dGUgb3ZlcmxhcCBhcmVhCmludGVyc2VjdGlvbl9nZW9tZXRyeSA8LSBzdF9pbnRlcnNlY3Rpb24ocG9seV9oZWFsdGh5X3NmYywgcG9seV9kaXNlYXNlZF9zZmMpCiMgQ29udmVydCBzZjo6dW5pdHMgdG8gbnVtZXJpYyBiZWZvcmUgb3BlcmF0aW9ucwppbnRlcnNlY3RfYXJlYSA8LSBhcy5udW1lcmljKHN0X2FyZWEoaW50ZXJzZWN0aW9uX2dlb21ldHJ5KSkKCiMgQ2FsY3VsYXRlIHRvdGFsIGFyZWEgZm9yIG5vcm1hbGl6YXRpb24KYXJlYV9oZWFsdGh5IDwtIGFzLm51bWVyaWMoc3RfYXJlYShwb2x5X2hlYWx0aHlfc2ZjKSkKYXJlYV9kaXNlYXNlZCA8LSBhcy5udW1lcmljKHN0X2FyZWEocG9seV9kaXNlYXNlZF9zZmMpKQoKIyAiVG90YWwgYXJlYSIgZm9yIG92ZXJsYXAgcGVyY2VudGFnZSB1c3VhbGx5IG1lYW5zIHRoZSBhcmVhIG9mIHRoZSBzbWFsbGVyIGVsbGlwc2UKIyBvciB0aGUgc3VtIG9mIGJvdGggYXJlYXMuIFVzZXIgc3BlY2lmaWVkIG1pbihhcmVhX2hlYWx0aHksIGFyZWFfZGlzZWFzZWQpLgp0b3RhbF9hcmVhX2Zvcl9vdmVybGFwX2NhbGMgPC0gbWluKGFyZWFfaGVhbHRoeSwgYXJlYV9kaXNlYXNlZCkKCgpvdmVybGFwX3ZhbHVlIDwtIGlmIChpc1RSVUUoIWlzLm5hKGludGVyc2VjdF9hcmVhKSAmJiB0b3RhbF9hcmVhX2Zvcl9vdmVybGFwX2NhbGMgPiAwKSkgeyhpbnRlcnNlY3RfYXJlYSAvIHRvdGFsX2FyZWFfZm9yX292ZXJsYXBfY2FsYykgKiAxMDAKfSBlbHNlIHsgMCB9CgojIEVuc3VyZSBvdmVybGFwX3ZhbHVlIGlzIG5vdCBOYU4gaWYgdGhlcmUncyBubyBpbnRlcnNlY3Rpb24KaWYgKGlzLm5hbihvdmVybGFwX3ZhbHVlKSkgewogIG92ZXJsYXBfdmFsdWUgPC0gMAp9CgoKIyAtLS0gUGxvdCBQQ0ExIHZzIFBDQTIgd2l0aCBjdXN0b21pemVkIHRoZW1lIGFuZCBvdmVybGFwIGFubm90YXRpb24gLS0tCmNhdCgiXG4tLS0gR2VuZXJhdGluZyBQQ0EgUGxvdCAtLS1cbiIpCnBjYV9wbG90IDwtIGdncGxvdChwY2Ffc2NvcmVzLCBhZXMoeCA9IFBDMSwgeSA9IFBDMiwgY29sb3IgPSBDb25kaXRpb24sIGZpbGwgPSBDb25kaXRpb24pKSArCiAgZ2VvbV9wb2ludChzaXplID0gMywgYWxwaGEgPSAwLjgpICsgIyBTY2F0dGVyIHBvaW50cyAjIENyZWF0ZSBhbmQgZmlsbCBlbGxpcHNlcwogIHN0YXRfZWxsaXBzZShnZW9tID0gInBvbHlnb24iLCBhbHBoYSA9IDAuMiwgbGluZXR5cGUgPSAic29saWQiLCBsZXZlbCA9IDAuOTUpICsKICBsYWJzKAogICAgdGl0bGUgPSBwYXN0ZTAoIlN5bnRoZXRpYyBHZW5lIEV4cHJlc3Npb24gRGF0YSAoRWZmZWN0OiAiLCBsb2dfbW9kdWxlX2VmZmVjdF9tYWduaXR1ZGUsICIpIiksCiAgICB4ID0gcGFzdGUwKCJQQzEgKCIsIHBjMV92YXJfZXhwbGFpbmVkLCAiJSkiKSwKICAgIHkgPSBwYXN0ZTAoIlBDMiAoIiwgcGMyX3Zhcl9leHBsYWluZWQsICIlKSIpCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksIyBSZW1vdmUgZ3JpZGxpbmVzCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiYmxhY2siKSwgIyBBZGQgYXhpcyBsaW5lcwogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLCAjIEluY3JlYXNlIGZvbnQgc2l6ZSBvZiBheGlzIGxhYmVscwogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICMgSW5jcmVhc2UgdGljayBsYWJlbCBzaXplCiAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwgIyBDZW50ZXIgdGl0bGUKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgICApCgojIEdldCBwbG90IGxpbWl0cyBhZnRlciBkZWZpbmluZyB0aGUgcGxvdCBvYmplY3QgJ3BjYV9wbG90JwojIFRoaXMgaXMgY3J1Y2lhbCBmb3IgY29ycmVjdGx5IHBvc2l0aW9uaW5nIHRoZSBhbm5vdGF0aW9uCnBsb3RfbGltaXRzIDwtIGdncGxvdF9idWlsZChwY2FfcGxvdCkkbGF5b3V0JHBhbmVsX3BhcmFtc1tbMV1dCnhtaW4gPC0gcGxvdF9saW1pdHMkeC5yYW5nZVsxXQp5bWluIDwtIHBsb3RfbGltaXRzJHkucmFuZ2VbMV0KCiMgQWRkIGFubm90YXRpb24gYXQgbG93ZXItbGVmdApmaW5hbF9wY2FfcGxvdCA8LSBwY2FfcGxvdCArCiAgYW5ub3RhdGUoInRleHQiLCB4ID0geG1pbiArIDAuMDUgKiAocGxvdF9saW1pdHMkeC5yYW5nZVsyXSAtIHhtaW4pLAogICAgICAgICAgIHkgPSB5bWluICsgMC4wNSAqIChwbG90X2xpbWl0cyR5LnJhbmdlWzJdIC0geW1pbiksCiAgICAgICAgICAgbGFiZWwgPSBzcHJpbnRmKCJPdmVybGFwID0gJS4yZiUlIiwgb3ZlcmxhcF92YWx1ZSksCiAgICAgICAgICAgc2l6ZSA9IDUsIGhqdXN0ID0gMCwgdmp1c3QgPSAwLCBjb2xvciA9ICJibGFjayIpCgpwcmludChmaW5hbF9wY2FfcGxvdCkKCiMgLS0tIFNhdmUgUENBIFBsb3QgLS0tCnBjYV9wbG90X2ZpbGVuYW1lIDwtIHBhc3RlMCgicGNhX3Bsb3RfZWZmZWN0XyIsIGxvZ19tb2R1bGVfZWZmZWN0X21hZ25pdHVkZSwgIi5wbmciKQpnZ3NhdmUocGNhX3Bsb3RfZmlsZW5hbWUsIHBsb3QgPSBmaW5hbF9wY2FfcGxvdCwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDAsIGJnID0gIndoaXRlIikKY2F0KHNwcmludGYoIlNhdmVkICVzXG5cbiIsIHBjYV9wbG90X2ZpbGVuYW1lKSkKCgojIFRvIHNlZSBob3cgbXVjaCB2YXJpYW5jZSBpcyBleHBsYWluZWQgYnkgdGhlIGZpcnN0IGZldyBQQ3M6CnByaW50KCJWYXJpYW5jZSBleHBsYWluZWQgYnkgUENzOiIpCnByaW50KHN1bW1hcnkocGNhX3Jlc3VsdHMpKQpjYXQocGFzdGUwKHJlcCgiLSIsIDUwKSwgY29sbGFwc2UgPSAiIiksICJcblxuIikKCgojIC0tLSA5LiBDb21wdXRlIE92ZXJsYXAgUGVyY2VudGFnZSAoRXN0aW1hdGVkIHVzaW5nIExvZ2lzdGljIFJlZ3Jlc3Npb24pIC0tLQojIFRoaXMgaXMgYSBzZXBhcmF0ZSBlc3RpbWF0aW9uIG9mIG92ZXJsYXAgZnJvbSB0aGUgZ2VvbWV0cmljIG9uZS4KCmNhdCgiXG4tLS0gQ29tcHV0aW5nIE92ZXJsYXAgUGVyY2VudGFnZSAoRXN0aW1hdGVkIHZpYSBMb2dpc3RpYyBSZWdyZXNzaW9uKSAtLS1cbiIpCgojIEVuc3VyZSAnQ29uZGl0aW9uJyBpcyBhIGZhY3RvciBmb3IgY2xhc3NpZmljYXRpb24KcGNhX3Njb3JlcyRDb25kaXRpb24gPC0gZmFjdG9yKHBjYV9zY29yZXMkQ29uZGl0aW9uKQoKIyBUcmFpbiBhIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwKbG9naXN0aWNfbW9kZWwgPC0gZ2xtKENvbmRpdGlvbiB+IFBDMSArIFBDMiwgZGF0YSA9IHBjYV9zY29yZXMsIGZhbWlseSA9ICJiaW5vbWlhbCIpCgojIFByZWRpY3QgcHJvYmFiaWxpdGllcwpwcm9iYWJpbGl0aWVzIDwtIHByZWRpY3QobG9naXN0aWNfbW9kZWwsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBDb252ZXJ0IHByb2JhYmlsaXRpZXMgdG8gY2xhc3MgcHJlZGljdGlvbnMKIyBUaGUgbGV2ZWxzIHNob3VsZCBtYXRjaCAnSGVhbHRoeScgYW5kICdEaXNlYXNlZCcKcHJlZGljdGVkX2NsYXNzZXNfbHIgPC0gZmFjdG9yKGlmZWxzZShwcm9iYWJpbGl0aWVzID4gMC41LCAiRGlzZWFzZWQiLCAiSGVhbHRoeSIpLCBsZXZlbHMgPSBsZXZlbHMocGNhX3Njb3JlcyRDb25kaXRpb24pKQoKIyBDYWxjdWxhdGUgbWlzY2xhc3NpZmljYXRpb24gcmF0ZQptaXNjbGFzc2lmaWNhdGlvbl9yYXRlX2xyIDwtIG1lYW4ocHJlZGljdGVkX2NsYXNzZXNfbHIgIT0gcGNhX3Njb3JlcyRDb25kaXRpb24pCm92ZXJsYXBfcGVyY2VudGFnZV9sciA8LSByb3VuZChtaXNjbGFzc2lmaWNhdGlvbl9yYXRlX2xyICogMTAwLCAyKQoKY2F0KHNwcmludGYoIkVzdGltYXRlZCBPdmVybGFwIFBlcmNlbnRhZ2UgKExvZ2lzdGljIFJlZ3Jlc3Npb24gbWlzY2xhc3NpZmljYXRpb24pOiAlLjJmJSVcbiIsIG92ZXJsYXBfcGVyY2VudGFnZV9scikpCmNhdChwYXN0ZTAocmVwKCI9IiwgNTApLCBjb2xsYXBzZSA9ICIiKSwgIlxuIikKCgpgYGAKCmBgYHtyfQpwbmcocGFzdGUwKCJoaXN0b2dyYW1fIiwgbG9nX21vZHVsZV9lZmZlY3RfbWFnbml0dWRlLCAiLnBuZyIpLCB3aWR0aCA9IDYsIGhlaWdodCA9IDUsIHVuaXRzID0gImluIiwgcmVzID0gNjAwKQpoaXN0KHJvd1N1bXMoc3ludGhldGljX2V4cHJlc3Npb25fZGF0YSksIGNvbD0icmVkIiwgeGxhYiA9ICJFZGdlIFN0cmVuZ3RoIikKZGV2Lm9mZigpCmBgYAoKIyBCLjAgRGF0YSBwcmVwcm9jZXNzaW5nCgpgYGB7cn0KI3JlbnY6Omluc3RhbGwoJ3RpYmJsZScpCmxpYnJhcnkodGliYmxlKQpsaWJyYXJ5KEJpb2Jhc2UpCmxpYnJhcnkoYW5ub3RhdGUpCgpwaGVuRGF0YSA9IEFubm90YXRlZERhdGFGcmFtZSgKICByZWFkLmNzdihwYXN0ZTAoInNhbXBsZV9tZXRhZGF0YV9lZmZlY3RfIix2YXJpYWJpbGl0eSwiLmNzdiIpLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gMSkKKQpgYGAKCmBgYHtyfQplU2V0ID0gbmV3KCJFeHByZXNzaW9uU2V0IiwKICAgICAgICAgICBleHBycz0gYXMubWF0cml4KHN5bnRoZXRpY19leHByZXNzaW9uX2RhdGEpLAogICAgICAgICAgIHBoZW5vRGF0YSA9IHBoZW5EYXRhCiAgICAgICAgICAgKQplU2V0CnN1bW1hcnkoZXhwcnMoZVNldCkpWywxOjVdCmBgYAoKYGBge3J9CiNyZW52OjppbnN0YWxsKCdsaW1tYScpCmxpYnJhcnkobGltbWEpCgpub3JtRGF0YSA9IG5vcm1hbGl6ZUJldHdlZW5BcnJheXMoZXhwcnMoZVNldCkpCmVTZXQubm9ybSA9IG5ldygiRXhwcmVzc2lvblNldCIsCiAgICAgICAgICAgZXhwcnM9IGFzLm1hdHJpeChub3JtRGF0YSksCiAgICAgICAgICAgcGhlbm9EYXRhID0gcGhlbkRhdGEpCmJveHBsb3QoZXhwcnMoZVNldC5ub3JtKVssMToxMF0pCnN1bW1hcnkoZXhwcnMoZVNldC5ub3JtKSlbLDE6NV0KYGBgCgpgYGB7cn0KbGlicmFyeShXR0NOQSkKZGF0Lm1hdHJpeCA9IGV4cHJzKGVTZXQubm9ybSkKZGF0LmV4cHIgPSBhcy5kYXRhLmZyYW1lKHQoZGF0Lm1hdHJpeCkpCgpnc2cgPSBnb29kU2FtcGxlc0dlbmVzKGRhdC5leHByLCB2ZXJib3NlID0gMykKCmlmKHN1bSghZ3NnJGdvb2RTYW1wbGVzKT4wKSB7CiAgc3VtbWFyeShleHBycyhlU2V0Lm5vcm0uY29tYmF0KSlbLCFnc2ckZ29vZFNhbXBsZXNdCn0Kc3ByaW50ZigiVGhlcmUgYXJlICVkIGJhZCBzYW1wbGVzIGluIHRoZSBkYXRhc2V0LiIsIHN1bSghZ3NnJGdvb2RTYW1wbGVzKSkKCmlmKHN1bSghZ3NnJGdvb2RHZW5lcykpIHsKICBiYWQuZ2VuZXMubGlzdCA9IGNvbG5hbWVzKGRhdC5leHByKVshZ3NnJGdvb2RHZW5lc10KfQpzcHJpbnRmKCdUaGVyZSBhcmUgJWQgYmFkIGdlbmVzIGluIHRoZSBkYXRhc2V0LicsIHN1bSghZ3NnJGdvb2RHZW5lcykpCmBgYAoKYGBge3J9CnNhbXBsZVRyZWUgPSBoY2x1c3QoZGlzdChkYXQuZXhwciksIG1ldGhvZCA9ICJhdmVyYWdlIikKcGxvdChzYW1wbGVUcmVlLCAKICAgICBtYWluID0gIlRyZWUgY2x1c3RlciBmb3Igb3V0bGllciBkZXRlY3Rpb24iLCBzdWI9IiIsIHhsYWI9IiIsIAogICAgIGNleC5sYWIgPSAxLjUsIGNleC5heGlzID0gMS41LCBjZXgubWFpbiA9IDIsCiAgICAgKQpoPTI1CmNsdXN0ID0gY3V0cmVlU3RhdGljKHNhbXBsZVRyZWUsIGN1dEhlaWdodCA9IGgsIG1pblNpemUgPSAxMCkKdGFibGUoY2x1c3QpCmBgYAoKIyBCLiBXR0NOQSBNZXRob2QKCmBgYHtyfQpsaWJyYXJ5KFdHQ05BKQpvcHRpb25zKHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKZGlzYWJsZVdHQ05BVGhyZWFkcygpCmBgYAoKYGBge3J9Cmdnc2V0ID0gYXMuZGF0YS5mcmFtZSh0KGV4cHJzKGVTZXQubm9ybSkpKQpoZWFkKGdnc2V0KQpgYGAKCmBgYHtyfQpzb2Z0UG93ZXIgPSAxCmFkaiA9IGFkamFjZW5jeShnZ3NldCwgcG93ZXIgPSBzb2Z0UG93ZXIpCmRpbShhZGopClRPTSA9IFRPTXNpbWlsYXJpdHkoYWRqKTsKZGlzc1RPTSA9IDEtVE9NCmRpbShUT00pCgojIENhbGwgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGZ1bmN0aW9uCmdlbmVUcmVlID0gaGNsdXN0KGFzLmRpc3QoZGlzc1RPTSksIG1ldGhvZCA9ICJhdmVyYWdlIik7CiMgUGxvdCB0aGUgcmVzdWx0aW5nIGNsdXN0ZXJpbmcgdHJlZSAoZGVuZHJvZ3JhbSkKc2l6ZUdyV2luZG93KDEyLDkpCnBsb3QoZ2VuZVRyZWUsIHhsYWI9IiIsIHN1Yj0iIiwgCiAgICAgbWFpbiA9ICJHZW5lIGNsdXN0ZXJpbmcgb2YgU3ludGhldGljIERhdGFzZXQiLAogICAgIGxhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wNCkKYGBgCgpgYGB7cn0KIyBXZSBsaWtlIGxhcmdlIG1vZHVsZXMsIHNvIHdlIHNldCB0aGUgbWluaW11bSBtb2R1bGUgc2l6ZSByZWxhdGl2ZWx5IGhpZ2g6Cm1pbk1vZHVsZVNpemUgPSAxMDsKCiMgTW9kdWxlIGlkZW50aWZpY2F0aW9uIHVzaW5nIGR5bmFtaWMgdHJlZSBjdXQ6CmR5bmFtaWNNb2RzID0gY3V0cmVlRHluYW1pYyhkZW5kcm8gPSBnZW5lVHJlZSwgZGlzdE0gPSBkaXNzVE9NLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVlcFNwbGl0ID0gMiwgcGFtUmVzcGVjdHNEZW5kcm8gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbkNsdXN0ZXJTaXplID0gbWluTW9kdWxlU2l6ZSkKdGFibGUoZHluYW1pY01vZHMpCgojIENvbnZlcnQgbnVtZXJpYyBsYWJsZXMgaW50byBjb2xvcnMKZHluYW1pY0NvbG9ycyA9IGxhYmVsczJjb2xvcnMoZHluYW1pY01vZHMpCnRhYmxlKGR5bmFtaWNDb2xvcnMpCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0gYW5kIGNvbG9ycyB1bmRlcm5lYXRoCnNpemVHcldpbmRvdyg4LDYpCnBsb3REZW5kcm9BbmRDb2xvcnMoZ2VuZVRyZWUsIGR5bmFtaWNDb2xvcnMsICJEeW5hbWljIFRyZWUgQ3V0IiwKZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLAphZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUsCm1haW4gPSAiR2VuZSBkZW5kcm9ncmFtIG9mIFN5bnRoZXRpYyBEYXRhc2V0IikKYGBgCgpgYGB7cn0KIyBDb25zdHJ1Y3QgbnVtZXJpY2FsIGxhYmVscyBjb3JyZXNwb25kaW5nIHRvIHRoZSBjb2xvcnMKY29sb3JPcmRlciA9IGMoImdyZXkiLCBzdGFuZGFyZENvbG9ycyg1MCkpOwptb2R1bGVMYWJlbHMgPSBtYXRjaChkeW5hbWljQ29sb3JzLCBjb2xvck9yZGVyKS0xOwp0YWJsZShkeW5hbWljQ29sb3JzKQp0YWJsZShkeW5hbWljTW9kcykKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lCmRmIDwtIGRhdGEuZnJhbWUoCiAgZ2VuZXMgPSBjb2xuYW1lcyhnZ3NldCksCiAgbW9kdWxlcyA9IGR5bmFtaWNDb2xvcnMKKQojIFNhdmUgdGhlIGRhdGFmcmFtZSB0byBhIENTViBmaWxlCm91dHB1dF9maWxlIDwtIHBhc3RlMCgibW9kdWxlQ29sb3JzX1dHQ05BXyIsdmFyaWFiaWxpdHksIi5jc3YiKQp3cml0ZS5jc3YoZGYsIGZpbGUgPSBvdXRwdXRfZmlsZSwgcm93Lm5hbWVzID0gRikKZGltKGRmKQpgYGAKCmBgYHtyfQpkYXRUcmFpdHMgPSBwRGF0YShwaGVuRGF0YSkKZGF0VHJhaXRzJENvbmRpdGlvbiA8LSBpZmVsc2UoZGF0VHJhaXRzJENvbmRpdGlvbiA9PSAiSGVhbHRoeSIsIDAsIDEpCmRhdFRyYWl0cwoKYGBgCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQoKZGF0RXhwciA9IGdnc2V0Cm5HZW5lcyA9IG5jb2woZGF0RXhwcikKblNhbXBsZXMgPSBucm93KGRhdEV4cHIpCmRhdFRyYWl0cyA9IHBEYXRhKGVTZXQubm9ybSkKZGF0VHJhaXRzJENvbmRpdGlvbiA8LSBpZmVsc2UoZGF0VHJhaXRzJENvbmRpdGlvbiA9PSAiSGVhbHRoeSIsIDAsIDEpCgpNRXMwID0gbW9kdWxlRWlnZW5nZW5lcyhkYXRFeHByLCBkeW5hbWljQ29sb3JzKSRlaWdlbmdlbmVzCk1FcyA9IG9yZGVyTUVzKE1FczApCm1vZHVsZVRyYWl0Q29yID0gY29yKE1FcywgZGF0VHJhaXRzLCB1c2UgPSAicCIpCm1vZHVsZVRyYWl0UHZhbHVlID0gY29yUHZhbHVlU3R1ZGVudChtb2R1bGVUcmFpdENvciwgblNhbXBsZXMpCgoKIyBXaWxsIGRpc3BsYXkgY29ycmVsYXRpb25zIGFuZCB0aGVpciBwLXZhbHVlcwpwbmcocGFzdGUwKCJXR0NOQU1vZHVsZXNfU3ludGhldGljXyIsIHZhcmlhYmlsaXR5LCAiLnBuZyIpLHdpZHRoPTcsaGVpZ2h0PTUsdW5pdHM9ImluIixyZXM9NjAwKQp0ZXh0TWF0cml4ID0gcGFzdGUoc2lnbmlmKG1vZHVsZVRyYWl0Q29yLCAyKSwgIlxuKCIsCiAgICAgICAgICAgICAgICAgICAgc2lnbmlmKG1vZHVsZVRyYWl0UHZhbHVlLCAxKSwgIikiLCBzZXAgPSAiIik7CiAgICAgICAgICAgICAgICAgICAgZGltKHRleHRNYXRyaXgpID0gZGltKG1vZHVsZVRyYWl0Q29yKQogICAgICAgICAgICAgICAgICAgIHBhcihtYXIgPSBjKDYsIDEwLCAzLCAzKSkKcGFyKG1hciA9IGMoOCwgOCwgMiwgMSkpICAjIFJlZHVjZSBtYXJnaW5zIChib3R0b20sIGxlZnQsIHRvcCwgcmlnaHQpCmxhYmVsZWRIZWF0bWFwKE1hdHJpeCA9IG1vZHVsZVRyYWl0Q29yLAogICAgICAgICAgICAgIHhMYWJlbHMgPSBuYW1lcyhkYXRUcmFpdHMpLAogICAgICAgICAgICAgIHlMYWJlbHMgPSBuYW1lcyhNRXMpLAogICAgICAgICAgICAgIHlTeW1ib2xzID0gbmFtZXMoTUVzKSwKICAgICAgICAgICAgICBjb2xvckxhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgIGNvbG9ycyA9IGJsdWVXaGl0ZVJlZCg1MCksCiAgICAgICAgICAgICAgdGV4dE1hdHJpeCA9IHRleHRNYXRyaXgsCiAgICAgICAgICAgICAgc2V0U3RkTWFyZ2lucyA9IEZBTFNFLAogICAgICAgICAgICAgIGNleC50ZXh0ID0gMC4zLAogICAgICAgICAgICAgIHpsaW0gPSBjKC0xLDEpLAogICAgICAgICAgICAgIG1haW4gPSBwYXN0ZSgiRGlzZWFzZWQgdnMgSGVhbHRoeSIpKQpgYGAKCiMgQy4gT3VyIE1ldGhvZAoKYGBge3J9CmhlYWx0aHkuZXhwcnMgPSBleHBycyhlU2V0Lm5vcm0pWywxOjUwXQp3cml0ZS50YWJsZShoZWFsdGh5LmV4cHJzLGZpbGUgPSBwYXN0ZTAoInN5bnRoZXRpY19oZWFsdGh5XyIsIGxvZ19tb2R1bGVfZWZmZWN0X21hZ25pdHVkZSwiLnR4dCIpLCBzZXAgPSAiXHQiLCAKICAgICAgICAgICAgICBxdW90ZSA9IEZBTFNFLCByb3cubmFtZXMgPSBUUlVFLCBjb2wubmFtZXMgPSBUUlVFKQoKZGlzZWFzZWQuZXhwcnMgPSBleHBycyhlU2V0Lm5vcm0pWyw1MToxMDBdCndyaXRlLnRhYmxlKGRpc2Vhc2VkLmV4cHJzLCBmaWxlID0gcGFzdGUwKCJzeW50aGV0aWNfZGlzZWFzZWRfIiwgbG9nX21vZHVsZV9lZmZlY3RfbWFnbml0dWRlLCIudHh0IiksIHNlcCA9ICJcdCIsIAogICAgICAgICAgICAgIHF1b3RlID0gRkFMU0UsIHJvdy5uYW1lcyA9IFRSVUUsIGNvbC5uYW1lcyA9IFRSVUUpCmRpbShoZWFsdGh5LmV4cHJzKQpkaW0oZGlzZWFzZWQuZXhwcnMpCmBgYAoKIyMgQy4xIExvdyB2YXJpYWJpbGl0eSAocCA9IDAuMSkKCmBgYHtyfQpoZWFsdGh5LmRjb3IgPSByZWFkLmNzdigiZGNvci1tZXRob2QvZGNvcl9zeW50aGV0aWNfaGVhbHRoeV8wLjEuY3N2IiwgaGVhZGVyPVQpCmhlYWx0aHkuZGNvciA9IGFzLm1hdHJpeChoZWFsdGh5LmRjb3JbLC1jKDEpXSkKZGltKGhlYWx0aHkuZGNvcikKYGBgCgpgYGB7cn0KVE9NLmhlYWx0aHkuZGNvciA9IFRPTXNpbWlsYXJpdHkoYXMubWF0cml4KGhlYWx0aHkuZGNvcikpOwpkaXNzVE9NLmhlYWx0aHkuZGNvciA9IDEtVE9NLmhlYWx0aHkuZGNvcgpkaW0oVE9NLmhlYWx0aHkuZGNvcikKYGBgCgpgYGB7cn0KaGlzdChyb3dTdW1zKFRPTS5oZWFsdGh5LmRjb3IpKQp3cml0ZS50YWJsZShkaXNzVE9NLmhlYWx0aHkuZGNvciwgZmlsZSA9ICJkY29yLW1ldGhvZC9kaXNzVE9NX2hlYWx0aHlfZGNvcjlfMC4xLnR4dCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgc2VwID0gIiAiKQpgYGAKCmBgYHtyfQojIENhbGwgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGZ1bmN0aW9uCmdlbmVUcmVlID0gaGNsdXN0KGFzLmRpc3QoZGlzc1RPTS5oZWFsdGh5LmRjb3IpLCBtZXRob2QgPSAiYXZlcmFnZSIpOwojIFBsb3QgdGhlIHJlc3VsdGluZyBjbHVzdGVyaW5nIHRyZWUgKGRlbmRyb2dyYW0pCnNpemVHcldpbmRvdygxMiw5KQpwbG90KGdlbmVUcmVlLCB4bGFiPSIiLCBzdWI9IiIsIAogICAgIG1haW4gPSAiR2VuZSBjbHVzdGVyaW5nIG9mIEhlYWx0aHkgU2FtcGxlcyBiYXNlZCBvbiBUT00tYmFzZWQgZGlzc2ltaWxhcml0eSIsCiAgICAgbGFiZWxzID0gRkFMU0UpCgojIFdlIGxpa2UgbGFyZ2UgbW9kdWxlcywgc28gd2Ugc2V0IHRoZSBtaW5pbXVtIG1vZHVsZSBzaXplIHJlbGF0aXZlbHkgaGlnaDoKbWluTW9kdWxlU2l6ZSA9IDEwOwoKIyBNb2R1bGUgaWRlbnRpZmljYXRpb24gdXNpbmcgZHluYW1pYyB0cmVlIGN1dDoKZHluYW1pY01vZHMgPSBjdXRyZWVEeW5hbWljKGRlbmRybyA9IGdlbmVUcmVlLCBkaXN0TSA9IGRpc3NUT00uaGVhbHRoeS5kY29yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVlcFNwbGl0ID0gMiwgcGFtUmVzcGVjdHNEZW5kcm8gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbkNsdXN0ZXJTaXplID0gbWluTW9kdWxlU2l6ZSkKdGFibGUoZHluYW1pY01vZHMpCgojIENvbnZlcnQgbnVtZXJpYyBsYWJsZXMgaW50byBjb2xvcnMKZHluYW1pY0NvbG9ycyA9IGxhYmVsczJjb2xvcnMoZHluYW1pY01vZHMpCnRhYmxlKGR5bmFtaWNDb2xvcnMpCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0gYW5kIGNvbG9ycyB1bmRlcm5lYXRoCnNpemVHcldpbmRvdyg4LDYpCnBsb3REZW5kcm9BbmRDb2xvcnMoZ2VuZVRyZWUsIGR5bmFtaWNDb2xvcnMsICJEeW5hbWljIFRyZWUgQ3V0IiwKZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLAphZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUsCm1haW4gPSAiR2VuZSBkZW5kcm9ncmFtIG9mIEhlYWx0aHlTYW1wbGVzIikKYGBgCgpgYGB7cn0KaGVhbHRoeS5nZ3NldCA9IHJlYWQudGFibGUocGFzdGUwKCJkY29yLW1ldGhvZC8iLCJzeW50aGV0aWNfaGVhbHRoeV8wLjEudHh0IiksIGhlYWRlciA9IFRSVUUsIHNlcD0iXHQiKQpyb3duYW1lcyhoZWFsdGh5Lmdnc2V0KSA9IGhlYWx0aHkuZ2dzZXQkR2VuZXMKaGVhbHRoeS5nZ3NldCA9IGFzLmRhdGEuZnJhbWUodChoZWFsdGh5Lmdnc2V0WywtMV0pKQpoZWFsdGh5Lmdnc2V0ID0gaGVhbHRoeS5nZ3NldFssY29sbmFtZXMoaGVhbHRoeS5kY29yKV0KaGVhZChoZWFsdGh5Lmdnc2V0KQoKIyBDYWxjdWxhdGUgZWlnZW5nZW5lcwpNRUxpc3QgPSBtb2R1bGVFaWdlbmdlbmVzKGFzLm1hdHJpeChoZWFsdGh5Lmdnc2V0KSwgY29sb3JzID0gZHluYW1pY0NvbG9ycykKTUVzID0gTUVMaXN0JGVpZ2VuZ2VuZXMKIyBDYWxjdWxhdGUgZGlzc2ltaWxhcml0eSBvZiBtb2R1bGUgZWlnZW5nZW5lcwpNRURpc3MgPSAxLWNvcihNRXMpOwojIENsdXN0ZXIgbW9kdWxlIGVpZ2VuZ2VuZXMKTUVUcmVlID0gaGNsdXN0KGFzLmRpc3QoTUVEaXNzKSwgbWV0aG9kID0gImF2ZXJhZ2UiKTsKIyBQbG90IHRoZSByZXN1bHQKc2l6ZUdyV2luZG93KDE2LCA4KQpwbG90KE1FVHJlZSwgbWFpbiA9ICJDbHVzdGVyaW5nIG9mIG1vZHVsZSBlaWdlbmdlbmVzIGluIEhlYWx0aHkgU2FtcGxlcyIsCnhsYWIgPSAiIiwgc3ViID0gIiIpCiMgTWVyZ2luZyBtb2R1bGVzCk1FRGlzc1RocmVzID0gMC4yCiMgUGxvdCB0aGUgY3V0IGxpbmUgaW50byB0aGUgZGVuZHJvZ3JhbQphYmxpbmUoaD1NRURpc3NUaHJlcywgY29sID0gInJlZCIpCgoKIyBSZW5hbWUgdG8gbW9kdWxlQ29sb3JzCm1vZHVsZUNvbG9ycyA9IGR5bmFtaWNDb2xvcnMKIyBDb25zdHJ1Y3QgbnVtZXJpY2FsIGxhYmVscyBjb3JyZXNwb25kaW5nIHRvIHRoZSBjb2xvcnMKY29sb3JPcmRlciA9IGMoImdyZXkiLCBzdGFuZGFyZENvbG9ycyg1MCkpOwptb2R1bGVMYWJlbHMgPSBtYXRjaChtb2R1bGVDb2xvcnMsIGNvbG9yT3JkZXIpLTE7CmRpbShNRXMpCgpmcmVxLnRhYiA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKG1vZHVsZUNvbG9ycykpCgpjb2xuYW1lcyhmcmVxLnRhYikgPC0gYygiTW9kdWxlcyIsICJNZW1iZXJzaGlwIikKZnJlcS50YWIgPSBmcmVxLnRhYltvcmRlcigtZnJlcS50YWIkTWVtYmVyc2hpcCksIF0Kcm93bmFtZXMoZnJlcS50YWIpIDwtIDE6bnJvdyhmcmVxLnRhYikKZnJlcS50YWIkTW9kdWxlcyA9IGZhY3RvcihmcmVxLnRhYiRNb2R1bGVzLCBsZXZlbHMgPSBmcmVxLnRhYiRNb2R1bGVzKQoKZ2dwbG90KGZyZXEudGFiLCBhZXMoeCA9IE1vZHVsZXMsIHkgPSBNZW1iZXJzaGlwLCBmaWxsID0gTW9kdWxlcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgIGNvbG9yID0gImdyZXkxIiwgc2l6ZSA9IDAuNSkgKwogIGxhYnModGl0bGUgPSAiTW9kdWxlIFNpemUgaW4gSGVhbHRoeSBQYXRpZW50cyIsCiAgICAgICB4ID0gIk1vZHVsZXMiLAogICAgICAgeSA9ICJNZW1iZXJzaGlwIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1hcy5jaGFyYWN0ZXIoZnJlcS50YWIkTW9kdWxlcykpICsgIyBTZXQgYmFyIGNvbG9ycwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKAogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgICAgICAgICMgUmVtb3ZlIGdyaWQgbGluZXMKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShzaXplID0gMC41KSwgICMgQWRqdXN0IGxpbmUgd2lkdGggb2YgYXhlcwogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvcj0nYmxhY2snLCBhbmdsZSA9IDkwKSwgIyBBZGp1c3QgZm9udCBzaXplIG9mIGF4aXMgdGV4dAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvcj0nYmxhY2snKSwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSAjIEFkanVzdCBmb250IHNpemUgb2YgYXhpcyBsYWJlbHMKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIFJlbW92ZSBsZWdlbmQgaWYgdW5uZWNlc3NhcnkKYGBgCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBleHRyYWN0IGdlbmVzIGJlbG9uZ2luZyB0byBlYWNoIG1vZHVsZQpnZW5lbGlzdCA9IG5hbWVzKGhlYWx0aHkuZ2dzZXQpCmdldF9tb2R1bGVfZ2VuZXMgPC0gZnVuY3Rpb24oZ2VuZWxpc3QsIG1vZHVsZUNvbG9ycykgewogIHVuaXF1ZV9jb2xvcnMgPC0gdW5pcXVlKG1vZHVsZUNvbG9ycykgIyBHZXQgdW5pcXVlIG1vZHVsZSBuYW1lcwogIG1vZHVsZV9nZW5lcyA8LSBsYXBwbHkodW5pcXVlX2NvbG9ycywgZnVuY3Rpb24oY29sb3IpIHsKICAgIHdoaWNoKG1vZHVsZUNvbG9ycyA9PSBjb2xvcikgIyBJbmRpY2VzIG9mIGdlbmVzIGluIHRoaXMgbW9kdWxlCiAgfSkKICBuYW1lcyhtb2R1bGVfZ2VuZXMpIDwtIHVuaXF1ZV9jb2xvcnMKICAjIE1hcCBpbmRpY2VzIGJhY2sgdG8gZ2VuZSBuYW1lcwogIG1vZHVsZV9nZW5lcyA8LSBsYXBwbHkobW9kdWxlX2dlbmVzLCBmdW5jdGlvbihpbmRpY2VzKSBnZW5lbGlzdFtpbmRpY2VzXSkKICByZXR1cm4obW9kdWxlX2dlbmVzKQp9CgpoZWFsdGh5X21vZHVsZXMgPC0gZ2V0X21vZHVsZV9nZW5lcyhjb2xuYW1lcyhoZWFsdGh5LmRjb3IpLCBtb2R1bGVDb2xvcnMpCgojIENyZWF0ZSBhIGRhdGFmcmFtZQpkZiA8LSBkYXRhLmZyYW1lKAogIGdlbmVzID0gY29sbmFtZXMoaGVhbHRoeS5kY29yKSwKICBtb2R1bGVzID0gbW9kdWxlQ29sb3JzCikKIyBTYXZlIHRoZSBkYXRhZnJhbWUgdG8gYSBDU1YgZmlsZQpvdXRwdXRfZmlsZSA8LSBwYXN0ZTAoImRjb3ItbWV0aG9kLyIsIm1vZHVsZXNfaGVhbHRoeV8wLjEuY3N2IikKd3JpdGUuY3N2KGRmLCBmaWxlID0gb3V0cHV0X2ZpbGUsIHJvdy5uYW1lcyA9IEYpCmxlbmd0aChkZiRnZW5lcykKdW5pcXVlKGRmJG1vZHVsZXMpCmhlYWQoZGYpCmBgYAoKYGBge3J9CmRpc2Vhc2VkLmRjb3IgPSByZWFkLmNzdigiZGNvci1tZXRob2QvZGNvcl9zeW50aGV0aWNfZGlzZWFzZWRfMC4xLmNzdiIsIGhlYWRlcj1UKQpkaXNlYXNlZC5kY29yID0gYXMubWF0cml4KGRpc2Vhc2VkLmRjb3JbLC1jKDEpXSkKZGltKGRpc2Vhc2VkLmRjb3IpCmBgYAoKYGBge3J9ClRPTS5kaXNlYXNlZC5kY29yID0gVE9Nc2ltaWxhcml0eShhcy5tYXRyaXgoZGlzZWFzZWQuZGNvcikpOwpkaXNzVE9NLmRpc2Vhc2VkLmRjb3IgPSAxLVRPTS5kaXNlYXNlZC5kY29yCmRpbShUT00uZGlzZWFzZWQuZGNvcikKYGBgCgpgYGB7cn0KaGlzdChyb3dTdW1zKFRPTS5kaXNlYXNlZC5kY29yKSkKd3JpdGUudGFibGUoZGlzc1RPTS5kaXNlYXNlZC5kY29yLCBmaWxlID0gImRjb3ItbWV0aG9kL2Rpc3NUT01fZGlzZWFzZWRfZGNvcjlfMC4xLnR4dCIsIHJvdy5uYW1lcyA9IEZBTFNFLCBjb2wubmFtZXMgPSBGQUxTRSwgc2VwID0gIiAiKQpgYGAKCmBgYHtyfQojIENhbGwgdGhlIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGZ1bmN0aW9uCmdlbmVUcmVlMiA9IGhjbHVzdChhcy5kaXN0KGRpc3NUT00uZGlzZWFzZWQuZGNvciksIG1ldGhvZCA9ICJhdmVyYWdlIik7CiMgUGxvdCB0aGUgcmVzdWx0aW5nIGNsdXN0ZXJpbmcgdHJlZSAoZGVuZHJvZ3JhbSkKc2l6ZUdyV2luZG93KDEyLDkpCnBsb3QoZ2VuZVRyZWUyLCB4bGFiPSIiLCBzdWI9IiIsIAogICAgIG1haW4gPSAiR2VuZSBjbHVzdGVyaW5nIG9mIEhlYWx0aHkgU2FtcGxlcyBiYXNlZCBvbiBUT00tYmFzZWQgZGlzc2ltaWxhcml0eSIsCiAgICAgbGFiZWxzID0gRkFMU0UpCgojIFdlIGxpa2UgbGFyZ2UgbW9kdWxlcywgc28gd2Ugc2V0IHRoZSBtaW5pbXVtIG1vZHVsZSBzaXplIHJlbGF0aXZlbHkgaGlnaDoKbWluTW9kdWxlU2l6ZSA9IDEwOwoKIyBNb2R1bGUgaWRlbnRpZmljYXRpb24gdXNpbmcgZHluYW1pYyB0cmVlIGN1dDoKZHluYW1pY01vZHMyID0gY3V0cmVlRHluYW1pYyhkZW5kcm8gPSBnZW5lVHJlZTIsIGRpc3RNID0gZGlzc1RPTS5kaXNlYXNlZC5kY29yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVlcFNwbGl0ID0gMiwgcGFtUmVzcGVjdHNEZW5kcm8gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbkNsdXN0ZXJTaXplID0gbWluTW9kdWxlU2l6ZSkKdGFibGUoZHluYW1pY01vZHMyKQoKIyBDb252ZXJ0IG51bWVyaWMgbGFibGVzIGludG8gY29sb3JzCmR5bmFtaWNDb2xvcnMyID0gbGFiZWxzMmNvbG9ycyhkeW5hbWljTW9kczIpCnRhYmxlKGR5bmFtaWNDb2xvcnMyKQoKIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCBjb2xvcnMgdW5kZXJuZWF0aApzaXplR3JXaW5kb3coOCw2KQpwbG90RGVuZHJvQW5kQ29sb3JzKGdlbmVUcmVlMiwgZHluYW1pY0NvbG9yczIsICJEeW5hbWljIFRyZWUgQ3V0IiwKZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLAphZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUsCm1haW4gPSAiR2VuZSBkZW5kcm9ncmFtIG9mIERpc2Vhc2VkIFNhbXBsZXMiKQpgYGAKCmBgYHtyfQpkaXNlYXNlZC5nZ3NldCA9IHJlYWQudGFibGUocGFzdGUwKCJkY29yLW1ldGhvZC8iLCJzeW50aGV0aWNfZGlzZWFzZWRfMC4xLnR4dCIpLCBoZWFkZXIgPSBUUlVFLCBzZXA9Ilx0IikKcm93bmFtZXMoZGlzZWFzZWQuZ2dzZXQpID0gZGlzZWFzZWQuZ2dzZXQkR2VuZXMKZGlzZWFzZWQuZ2dzZXQgPSBhcy5kYXRhLmZyYW1lKHQoZGlzZWFzZWQuZ2dzZXRbLC0xXSkpCmRpc2Vhc2VkLmdnc2V0ID0gZGlzZWFzZWQuZ2dzZXRbLGNvbG5hbWVzKGRpc2Vhc2VkLmRjb3IpXQpoZWFkKGRpc2Vhc2VkLmdnc2V0KQoKIyBDYWxjdWxhdGUgZWlnZW5nZW5lcwpNRUxpc3QyID0gbW9kdWxlRWlnZW5nZW5lcyhhcy5tYXRyaXgoZGlzZWFzZWQuZ2dzZXQpLCBjb2xvcnMgPSBkeW5hbWljQ29sb3JzMikKTUVzMiA9IE1FTGlzdDIkZWlnZW5nZW5lcwojIENhbGN1bGF0ZSBkaXNzaW1pbGFyaXR5IG9mIG1vZHVsZSBlaWdlbmdlbmVzCk1FRGlzczIgPSAxLWNvcihNRXMyKTsKIyBDbHVzdGVyIG1vZHVsZSBlaWdlbmdlbmVzCk1FVHJlZTIgPSBoY2x1c3QoYXMuZGlzdChNRURpc3MyKSwgbWV0aG9kID0gImF2ZXJhZ2UiKTsKIyBQbG90IHRoZSByZXN1bHQKc2l6ZUdyV2luZG93KDE2LCA4KQpwbG90KE1FVHJlZTIsIG1haW4gPSAiQ2x1c3RlcmluZyBvZiBtb2R1bGUgZWlnZW5nZW5lcyBpbiBIZWFsdGh5IFNhbXBsZXMiLAp4bGFiID0gIiIsIHN1YiA9ICIiKQojIE1lcmdpbmcgbW9kdWxlcwpNRURpc3NUaHJlcyA9IDAuMgojIFBsb3QgdGhlIGN1dCBsaW5lIGludG8gdGhlIGRlbmRyb2dyYW0KYWJsaW5lKGg9TUVEaXNzVGhyZXMsIGNvbCA9ICJyZWQiKQoKCiMgUmVuYW1lIHRvIG1vZHVsZUNvbG9ycwptb2R1bGVDb2xvcnMyID0gZHluYW1pY0NvbG9yczIKIyBDb25zdHJ1Y3QgbnVtZXJpY2FsIGxhYmVscyBjb3JyZXNwb25kaW5nIHRvIHRoZSBjb2xvcnMKY29sb3JPcmRlcjIgPSBjKCJncmV5Iiwgc3RhbmRhcmRDb2xvcnMoNTApKTsKbW9kdWxlTGFiZWxzMiA9IG1hdGNoKG1vZHVsZUNvbG9yczIsIGNvbG9yT3JkZXIyKS0xOwpkaW0oTUVzMikKCmZyZXEudGFiIDwtIGFzLmRhdGEuZnJhbWUodGFibGUobW9kdWxlQ29sb3JzMikpCgpjb2xuYW1lcyhmcmVxLnRhYikgPC0gYygiTW9kdWxlcyIsICJNZW1iZXJzaGlwIikKZnJlcS50YWIgPSBmcmVxLnRhYltvcmRlcigtZnJlcS50YWIkTWVtYmVyc2hpcCksIF0Kcm93bmFtZXMoZnJlcS50YWIpIDwtIDE6bnJvdyhmcmVxLnRhYikKZnJlcS50YWIkTW9kdWxlcyA9IGZhY3RvcihmcmVxLnRhYiRNb2R1bGVzLCBsZXZlbHMgPSBmcmVxLnRhYiRNb2R1bGVzKQoKZ2dwbG90KGZyZXEudGFiLCBhZXMoeCA9IE1vZHVsZXMsIHkgPSBNZW1iZXJzaGlwLCBmaWxsID0gTW9kdWxlcykpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgIGNvbG9yID0gImdyZXkxIiwgc2l6ZSA9IDAuNSkgKwogIGxhYnModGl0bGUgPSAiTW9kdWxlIFNpemUgaW4gSGVhbHRoeSBQYXRpZW50cyIsCiAgICAgICB4ID0gIk1vZHVsZXMiLAogICAgICAgeSA9ICJNZW1iZXJzaGlwIikgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1hcy5jaGFyYWN0ZXIoZnJlcS50YWIkTW9kdWxlcykpICsgIyBTZXQgYmFyIGNvbG9ycwogIHRoZW1lX21pbmltYWwoKSArIAogIHRoZW1lKAogICAgcGFuZWwuZ3JpZCA9IGVsZW1lbnRfYmxhbmsoKSwgICAgICAgICMgUmVtb3ZlIGdyaWQgbGluZXMKICAgIGF4aXMubGluZSA9IGVsZW1lbnRfbGluZShzaXplID0gMC41KSwgICMgQWRqdXN0IGxpbmUgd2lkdGggb2YgYXhlcwogICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvcj0nYmxhY2snLCBhbmdsZSA9IDkwKSwgIyBBZGp1c3QgZm9udCBzaXplIG9mIGF4aXMgdGV4dAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBjb2xvcj0nYmxhY2snKSwKICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSAjIEFkanVzdCBmb250IHNpemUgb2YgYXhpcyBsYWJlbHMKICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICAjIFJlbW92ZSBsZWdlbmQgaWYgdW5uZWNlc3NhcnkKYGBgCgpgYGB7cn0KIyBGdW5jdGlvbiB0byBleHRyYWN0IGdlbmVzIGJlbG9uZ2luZyB0byBlYWNoIG1vZHVsZQpnZW5lbGlzdDIgPSBuYW1lcyhkaXNlYXNlZC5nZ3NldCkKZ2V0X21vZHVsZV9nZW5lcyA8LSBmdW5jdGlvbihnZW5lbGlzdCwgbW9kdWxlQ29sb3JzKSB7CiAgdW5pcXVlX2NvbG9ycyA8LSB1bmlxdWUobW9kdWxlQ29sb3JzKSAjIEdldCB1bmlxdWUgbW9kdWxlIG5hbWVzCiAgbW9kdWxlX2dlbmVzIDwtIGxhcHBseSh1bmlxdWVfY29sb3JzLCBmdW5jdGlvbihjb2xvcikgewogICAgd2hpY2gobW9kdWxlQ29sb3JzID09IGNvbG9yKSAjIEluZGljZXMgb2YgZ2VuZXMgaW4gdGhpcyBtb2R1bGUKICB9KQogIG5hbWVzKG1vZHVsZV9nZW5lcykgPC0gdW5pcXVlX2NvbG9ycwogICMgTWFwIGluZGljZXMgYmFjayB0byBnZW5lIG5hbWVzCiAgbW9kdWxlX2dlbmVzIDwtIGxhcHBseShtb2R1bGVfZ2VuZXMsIGZ1bmN0aW9uKGluZGljZXMpIGdlbmVsaXN0W2luZGljZXNdKQogIHJldHVybihtb2R1bGVfZ2VuZXMpCn0KCmRpc2Vhc2VkX21vZHVsZXMgPC0gZ2V0X21vZHVsZV9nZW5lcyhjb2xuYW1lcyhkaXNlYXNlZC5kY29yKSwgbW9kdWxlQ29sb3JzMikKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lCmRmIDwtIGRhdGEuZnJhbWUoCiAgZ2VuZXMgPSBjb2xuYW1lcyhkaXNlYXNlZC5kY29yKSwKICBtb2R1bGVzID0gbW9kdWxlQ29sb3JzMgopCiMgU2F2ZSB0aGUgZGF0YWZyYW1lIHRvIGEgQ1NWIGZpbGUKb3V0cHV0X2ZpbGUgPC0gcGFzdGUwKCJkY29yLW1ldGhvZC8iLCJtb2R1bGVzX2Rpc2Vhc2VkXzAuMS5jc3YiKQp3cml0ZS5jc3YoZGYsIGZpbGUgPSBvdXRwdXRfZmlsZSwgcm93Lm5hbWVzID0gRikKbGVuZ3RoKGRmJGdlbmVzKQp1bmlxdWUoZGYkbW9kdWxlcykKaGVhZChkZikKYGBgCgojIyBDLjIgSGlnaCB2YXJpYWJpbGl0eSAocD0wLjkpCgpgYGB7cn0KaGVhbHRoeS5kY29yID0gcmVhZC5jc3YoImRjb3ItbWV0aG9kL2Rjb3Jfc3ludGhldGljX2hlYWx0aHlfMC45LmNzdiIsIGhlYWRlcj1UKQpoZWFsdGh5LmRjb3IgPSBhcy5tYXRyaXgoaGVhbHRoeS5kY29yWywtYygxKV0pCmRpbShoZWFsdGh5LmRjb3IpCmBgYAoKYGBge3J9Cmhpc3Qocm93U3Vtcyhhcy5tYXRyaXgoaGVhbHRoeS5kY29yKSkpCmFkaiA9IGFkamFjZW5jeS5mcm9tU2ltaWxhcml0eShhcy5tYXRyaXgoaGVhbHRoeS5kY29yKSwgcG93ZXI9MSkKaGlzdChyb3dTdW1zKGFkaikpCmBgYAoKYGBge3J9ClRPTS5oZWFsdGh5LmRjb3IgPSBUT01zaW1pbGFyaXR5KGFkaik7CmRpc3NUT00uaGVhbHRoeS5kY29yID0gMS1UT00uaGVhbHRoeS5kY29yCmRpbShUT00uaGVhbHRoeS5kY29yKQpgYGAKCmBgYHtyfQpoaXN0KHJvd1N1bXMoVE9NLmhlYWx0aHkuZGNvcikpCndyaXRlLnRhYmxlKGRpc3NUT00uaGVhbHRoeS5kY29yLCBmaWxlID0gImRjb3ItbWV0aG9kL2Rpc3NUT01faGVhbHRoeV9kY29yOV8wLjkudHh0Iiwgcm93Lm5hbWVzID0gRkFMU0UsIGNvbC5uYW1lcyA9IEZBTFNFLCBzZXAgPSAiICIpCmBgYAoKYGBge3J9CiMgQ2FsbCB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgZnVuY3Rpb24KZ2VuZVRyZWUgPSBoY2x1c3QoYXMuZGlzdChkaXNzVE9NLmhlYWx0aHkuZGNvciksIG1ldGhvZCA9ICJhdmVyYWdlIik7CiMgUGxvdCB0aGUgcmVzdWx0aW5nIGNsdXN0ZXJpbmcgdHJlZSAoZGVuZHJvZ3JhbSkKc2l6ZUdyV2luZG93KDEyLDkpCnBsb3QoZ2VuZVRyZWUsIHhsYWI9IiIsIHN1Yj0iIiwgCiAgICAgbWFpbiA9ICJHZW5lIGNsdXN0ZXJpbmcgb2YgSGVhbHRoeSBTYW1wbGVzIGJhc2VkIG9uIFRPTS1iYXNlZCBkaXNzaW1pbGFyaXR5IiwKICAgICBsYWJlbHMgPSBGQUxTRSkKCiMgV2UgbGlrZSBsYXJnZSBtb2R1bGVzLCBzbyB3ZSBzZXQgdGhlIG1pbmltdW0gbW9kdWxlIHNpemUgcmVsYXRpdmVseSBoaWdoOgptaW5Nb2R1bGVTaXplID0gMTA7CgojIE1vZHVsZSBpZGVudGlmaWNhdGlvbiB1c2luZyBkeW5hbWljIHRyZWUgY3V0OgpkeW5hbWljTW9kcyA9IGN1dHJlZUR5bmFtaWMoZGVuZHJvID0gZ2VuZVRyZWUsIGRpc3RNID0gZGlzc1RPTS5oZWFsdGh5LmRjb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZWVwU3BsaXQgPSAyLCBwYW1SZXNwZWN0c0RlbmRybyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluQ2x1c3RlclNpemUgPSBtaW5Nb2R1bGVTaXplKQp0YWJsZShkeW5hbWljTW9kcykKCiMgQ29udmVydCBudW1lcmljIGxhYmxlcyBpbnRvIGNvbG9ycwpkeW5hbWljQ29sb3JzID0gbGFiZWxzMmNvbG9ycyhkeW5hbWljTW9kcykKdGFibGUoZHluYW1pY0NvbG9ycykKCiMgUGxvdCB0aGUgZGVuZHJvZ3JhbSBhbmQgY29sb3JzIHVuZGVybmVhdGgKc2l6ZUdyV2luZG93KDgsNikKcGxvdERlbmRyb0FuZENvbG9ycyhnZW5lVHJlZSwgZHluYW1pY0NvbG9ycywgIkR5bmFtaWMgVHJlZSBDdXQiLApkZW5kcm9MYWJlbHMgPSBGQUxTRSwgaGFuZyA9IDAuMDMsCmFkZEd1aWRlID0gVFJVRSwgZ3VpZGVIYW5nID0gMC4wNSwKbWFpbiA9ICJHZW5lIGRlbmRyb2dyYW0gb2YgSGVhbHRoeVNhbXBsZXMiKQpgYGAKCmBgYHtyfQpoZWFsdGh5Lmdnc2V0ID0gcmVhZC50YWJsZShwYXN0ZTAoImRjb3ItbWV0aG9kLyIsInN5bnRoZXRpY19oZWFsdGh5XzAuOS50eHQiKSwgaGVhZGVyID0gVFJVRSwgc2VwPSJcdCIpCnJvd25hbWVzKGhlYWx0aHkuZ2dzZXQpID0gaGVhbHRoeS5nZ3NldCRHZW5lcwpoZWFsdGh5Lmdnc2V0ID0gYXMuZGF0YS5mcmFtZSh0KGhlYWx0aHkuZ2dzZXRbLC0xXSkpCmhlYWx0aHkuZ2dzZXQgPSBoZWFsdGh5Lmdnc2V0Wyxjb2xuYW1lcyhoZWFsdGh5LmRjb3IpXQpoZWFkKGhlYWx0aHkuZ2dzZXQpCgojIENhbGN1bGF0ZSBlaWdlbmdlbmVzCk1FTGlzdCA9IG1vZHVsZUVpZ2VuZ2VuZXMoYXMubWF0cml4KGhlYWx0aHkuZ2dzZXQpLCBjb2xvcnMgPSBkeW5hbWljQ29sb3JzKQpNRXMgPSBNRUxpc3QkZWlnZW5nZW5lcwojIENhbGN1bGF0ZSBkaXNzaW1pbGFyaXR5IG9mIG1vZHVsZSBlaWdlbmdlbmVzCk1FRGlzcyA9IDEtY29yKE1Fcyk7CiMgQ2x1c3RlciBtb2R1bGUgZWlnZW5nZW5lcwpNRVRyZWUgPSBoY2x1c3QoYXMuZGlzdChNRURpc3MpLCBtZXRob2QgPSAiYXZlcmFnZSIpOwojIFBsb3QgdGhlIHJlc3VsdApzaXplR3JXaW5kb3coMTYsIDgpCnBsb3QoTUVUcmVlLCBtYWluID0gIkNsdXN0ZXJpbmcgb2YgbW9kdWxlIGVpZ2VuZ2VuZXMgaW4gSGVhbHRoeSBTYW1wbGVzIiwKeGxhYiA9ICIiLCBzdWIgPSAiIikKIyBNZXJnaW5nIG1vZHVsZXMKTUVEaXNzVGhyZXMgPSAwLjIKIyBQbG90IHRoZSBjdXQgbGluZSBpbnRvIHRoZSBkZW5kcm9ncmFtCmFibGluZShoPU1FRGlzc1RocmVzLCBjb2wgPSAicmVkIikKCgojIFJlbmFtZSB0byBtb2R1bGVDb2xvcnMKbW9kdWxlQ29sb3JzID0gZHluYW1pY0NvbG9ycwojIENvbnN0cnVjdCBudW1lcmljYWwgbGFiZWxzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIGNvbG9ycwpjb2xvck9yZGVyID0gYygiZ3JleSIsIHN0YW5kYXJkQ29sb3JzKDUwKSk7Cm1vZHVsZUxhYmVscyA9IG1hdGNoKG1vZHVsZUNvbG9ycywgY29sb3JPcmRlciktMTsKZGltKE1FcykKCmZyZXEudGFiIDwtIGFzLmRhdGEuZnJhbWUodGFibGUobW9kdWxlQ29sb3JzKSkKCmNvbG5hbWVzKGZyZXEudGFiKSA8LSBjKCJNb2R1bGVzIiwgIk1lbWJlcnNoaXAiKQpmcmVxLnRhYiA9IGZyZXEudGFiW29yZGVyKC1mcmVxLnRhYiRNZW1iZXJzaGlwKSwgXQpyb3duYW1lcyhmcmVxLnRhYikgPC0gMTpucm93KGZyZXEudGFiKQpmcmVxLnRhYiRNb2R1bGVzID0gZmFjdG9yKGZyZXEudGFiJE1vZHVsZXMsIGxldmVscyA9IGZyZXEudGFiJE1vZHVsZXMpCgpnZ3Bsb3QoZnJlcS50YWIsIGFlcyh4ID0gTW9kdWxlcywgeSA9IE1lbWJlcnNoaXAsIGZpbGwgPSBNb2R1bGVzKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCAgY29sb3IgPSAiZ3JleTEiLCBzaXplID0gMC41KSArCiAgbGFicyh0aXRsZSA9ICJNb2R1bGUgU2l6ZSBpbiBIZWFsdGh5IFBhdGllbnRzIiwKICAgICAgIHggPSAiTW9kdWxlcyIsCiAgICAgICB5ID0gIk1lbWJlcnNoaXAiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWFzLmNoYXJhY3RlcihmcmVxLnRhYiRNb2R1bGVzKSkgKyAjIFNldCBiYXIgY29sb3JzCiAgdGhlbWVfbWluaW1hbCgpICsgCiAgdGhlbWUoCiAgICBwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLCAgICAgICAgIyBSZW1vdmUgZ3JpZCBsaW5lcwogICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKHNpemUgPSAwLjUpLCAgIyBBZGp1c3QgbGluZSB3aWR0aCBvZiBheGVzCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG9yPSdibGFjaycsIGFuZ2xlID0gOTApLCAjIEFkanVzdCBmb250IHNpemUgb2YgYXhpcyB0ZXh0CiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGNvbG9yPSdibGFjaycpLAogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpICMgQWRqdXN0IGZvbnQgc2l6ZSBvZiBheGlzIGxhYmVscwogICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgICMgUmVtb3ZlIGxlZ2VuZCBpZiB1bm5lY2Vzc2FyeQpgYGAKCmBgYHtyfQojIEZ1bmN0aW9uIHRvIGV4dHJhY3QgZ2VuZXMgYmVsb25naW5nIHRvIGVhY2ggbW9kdWxlCmdlbmVsaXN0ID0gbmFtZXMoaGVhbHRoeS5nZ3NldCkKZ2V0X21vZHVsZV9nZW5lcyA8LSBmdW5jdGlvbihnZW5lbGlzdCwgbW9kdWxlQ29sb3JzKSB7CiAgdW5pcXVlX2NvbG9ycyA8LSB1bmlxdWUobW9kdWxlQ29sb3JzKSAjIEdldCB1bmlxdWUgbW9kdWxlIG5hbWVzCiAgbW9kdWxlX2dlbmVzIDwtIGxhcHBseSh1bmlxdWVfY29sb3JzLCBmdW5jdGlvbihjb2xvcikgewogICAgd2hpY2gobW9kdWxlQ29sb3JzID09IGNvbG9yKSAjIEluZGljZXMgb2YgZ2VuZXMgaW4gdGhpcyBtb2R1bGUKICB9KQogIG5hbWVzKG1vZHVsZV9nZW5lcykgPC0gdW5pcXVlX2NvbG9ycwogICMgTWFwIGluZGljZXMgYmFjayB0byBnZW5lIG5hbWVzCiAgbW9kdWxlX2dlbmVzIDwtIGxhcHBseShtb2R1bGVfZ2VuZXMsIGZ1bmN0aW9uKGluZGljZXMpIGdlbmVsaXN0W2luZGljZXNdKQogIHJldHVybihtb2R1bGVfZ2VuZXMpCn0KCmhlYWx0aHlfbW9kdWxlcyA8LSBnZXRfbW9kdWxlX2dlbmVzKGNvbG5hbWVzKGhlYWx0aHkuZGNvciksIG1vZHVsZUNvbG9ycykKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lCmRmIDwtIGRhdGEuZnJhbWUoCiAgZ2VuZXMgPSBjb2xuYW1lcyhoZWFsdGh5LmRjb3IpLAogIG1vZHVsZXMgPSBtb2R1bGVDb2xvcnMKKQojIFNhdmUgdGhlIGRhdGFmcmFtZSB0byBhIENTViBmaWxlCm91dHB1dF9maWxlIDwtIHBhc3RlMCgiZGNvci1tZXRob2QvIiwibW9kdWxlc19oZWFsdGh5XzAuOS5jc3YiKQp3cml0ZS5jc3YoZGYsIGZpbGUgPSBvdXRwdXRfZmlsZSwgcm93Lm5hbWVzID0gRikKbGVuZ3RoKGRmJGdlbmVzKQp1bmlxdWUoZGYkbW9kdWxlcykKaGVhZChkZikKYGBgCgpgYGB7cn0KZGlzZWFzZWQuZGNvciA9IHJlYWQuY3N2KCJkY29yLW1ldGhvZC9kY29yX3N5bnRoZXRpY19kaXNlYXNlZF8wLjkuY3N2IiwgaGVhZGVyPVQpCmRpc2Vhc2VkLmRjb3IgPSBhcy5tYXRyaXgoZGlzZWFzZWQuZGNvclssLWMoMSldKQpkaW0oZGlzZWFzZWQuZGNvcikKYGBgCgpgYGB7cn0KaGlzdChyb3dTdW1zKGFzLm1hdHJpeChkaXNlYXNlZC5kY29yKSkpCmFkaiA9IGFkamFjZW5jeS5mcm9tU2ltaWxhcml0eShhcy5tYXRyaXgoZGlzZWFzZWQuZGNvciksIHBvd2VyPTEpCmhpc3Qocm93U3VtcyhhZGopKQpgYGAKCmBgYHtyfQpUT00uZGlzZWFzZWQuZGNvciA9IFRPTXNpbWlsYXJpdHkoYWRqKTsKZGlzc1RPTS5kaXNlYXNlZC5kY29yID0gMS1UT00uZGlzZWFzZWQuZGNvcgpkaW0oVE9NLmRpc2Vhc2VkLmRjb3IpCmBgYAoKYGBge3J9Cmhpc3Qocm93U3VtcyhUT00uZGlzZWFzZWQuZGNvcikpCndyaXRlLnRhYmxlKGRpc3NUT00uZGlzZWFzZWQuZGNvciwgZmlsZSA9ICJkY29yLW1ldGhvZC9kaXNzVE9NX2Rpc2Vhc2VkX2Rjb3I5XzAuOS50eHQiLCByb3cubmFtZXMgPSBGQUxTRSwgY29sLm5hbWVzID0gRkFMU0UsIHNlcCA9ICIgIikKYGBgCgpgYGB7cn0KIyBDYWxsIHRoZSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZyBmdW5jdGlvbgpnZW5lVHJlZTIgPSBoY2x1c3QoYXMuZGlzdChkaXNzVE9NLmRpc2Vhc2VkLmRjb3IpLCBtZXRob2QgPSAiYXZlcmFnZSIpOwojIFBsb3QgdGhlIHJlc3VsdGluZyBjbHVzdGVyaW5nIHRyZWUgKGRlbmRyb2dyYW0pCnNpemVHcldpbmRvdygxMiw5KQpwbG90KGdlbmVUcmVlMiwgeGxhYj0iIiwgc3ViPSIiLCAKICAgICBtYWluID0gIkdlbmUgY2x1c3RlcmluZyBvZiBIZWFsdGh5IFNhbXBsZXMgYmFzZWQgb24gVE9NLWJhc2VkIGRpc3NpbWlsYXJpdHkiLAogICAgIGxhYmVscyA9IEZBTFNFKQoKIyBXZSBsaWtlIGxhcmdlIG1vZHVsZXMsIHNvIHdlIHNldCB0aGUgbWluaW11bSBtb2R1bGUgc2l6ZSByZWxhdGl2ZWx5IGhpZ2g6Cm1pbk1vZHVsZVNpemUgPSAxMDsKCiMgTW9kdWxlIGlkZW50aWZpY2F0aW9uIHVzaW5nIGR5bmFtaWMgdHJlZSBjdXQ6CmR5bmFtaWNNb2RzMiA9IGN1dHJlZUR5bmFtaWMoZGVuZHJvID0gZ2VuZVRyZWUyLCBkaXN0TSA9IGRpc3NUT00uZGlzZWFzZWQuZGNvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZXBTcGxpdCA9IDIsIHBhbVJlc3BlY3RzRGVuZHJvID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5DbHVzdGVyU2l6ZSA9IG1pbk1vZHVsZVNpemUpCnRhYmxlKGR5bmFtaWNNb2RzMikKCiMgQ29udmVydCBudW1lcmljIGxhYmxlcyBpbnRvIGNvbG9ycwpkeW5hbWljQ29sb3JzMiA9IGxhYmVsczJjb2xvcnMoZHluYW1pY01vZHMyKQp0YWJsZShkeW5hbWljQ29sb3JzMikKCiMgUGxvdCB0aGUgZGVuZHJvZ3JhbSBhbmQgY29sb3JzIHVuZGVybmVhdGgKc2l6ZUdyV2luZG93KDgsNikKcGxvdERlbmRyb0FuZENvbG9ycyhnZW5lVHJlZTIsIGR5bmFtaWNDb2xvcnMyLCAiRHluYW1pYyBUcmVlIEN1dCIsCmRlbmRyb0xhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wMywKYWRkR3VpZGUgPSBUUlVFLCBndWlkZUhhbmcgPSAwLjA1LAptYWluID0gIkdlbmUgZGVuZHJvZ3JhbSBvZiBEaXNlYXNlZCBTYW1wbGVzIikKYGBgCgpgYGB7cn0KZGlzZWFzZWQuZ2dzZXQgPSByZWFkLnRhYmxlKHBhc3RlMCgiZGNvci1tZXRob2QvIiwic3ludGhldGljX2Rpc2Vhc2VkXzAuOS50eHQiKSwgaGVhZGVyID0gVFJVRSwgc2VwPSJcdCIpCnJvd25hbWVzKGRpc2Vhc2VkLmdnc2V0KSA9IGRpc2Vhc2VkLmdnc2V0JEdlbmVzCmRpc2Vhc2VkLmdnc2V0ID0gYXMuZGF0YS5mcmFtZSh0KGRpc2Vhc2VkLmdnc2V0WywtMV0pKQpkaXNlYXNlZC5nZ3NldCA9IGRpc2Vhc2VkLmdnc2V0Wyxjb2xuYW1lcyhkaXNlYXNlZC5kY29yKV0KaGVhZChkaXNlYXNlZC5nZ3NldCkKCiMgQ2FsY3VsYXRlIGVpZ2VuZ2VuZXMKTUVMaXN0MiA9IG1vZHVsZUVpZ2VuZ2VuZXMoYXMubWF0cml4KGRpc2Vhc2VkLmdnc2V0KSwgY29sb3JzID0gZHluYW1pY0NvbG9yczIpCk1FczIgPSBNRUxpc3QyJGVpZ2VuZ2VuZXMKIyBDYWxjdWxhdGUgZGlzc2ltaWxhcml0eSBvZiBtb2R1bGUgZWlnZW5nZW5lcwpNRURpc3MyID0gMS1jb3IoTUVzMik7CiMgQ2x1c3RlciBtb2R1bGUgZWlnZW5nZW5lcwpNRVRyZWUyID0gaGNsdXN0KGFzLmRpc3QoTUVEaXNzMiksIG1ldGhvZCA9ICJhdmVyYWdlIik7CiMgUGxvdCB0aGUgcmVzdWx0CnNpemVHcldpbmRvdygxNiwgOCkKcGxvdChNRVRyZWUyLCBtYWluID0gIkNsdXN0ZXJpbmcgb2YgbW9kdWxlIGVpZ2VuZ2VuZXMgaW4gSGVhbHRoeSBTYW1wbGVzIiwKeGxhYiA9ICIiLCBzdWIgPSAiIikKIyBNZXJnaW5nIG1vZHVsZXMKTUVEaXNzVGhyZXMgPSAwLjIKIyBQbG90IHRoZSBjdXQgbGluZSBpbnRvIHRoZSBkZW5kcm9ncmFtCmFibGluZShoPU1FRGlzc1RocmVzLCBjb2wgPSAicmVkIikKCgojIFJlbmFtZSB0byBtb2R1bGVDb2xvcnMKbW9kdWxlQ29sb3JzMiA9IGR5bmFtaWNDb2xvcnMyCiMgQ29uc3RydWN0IG51bWVyaWNhbCBsYWJlbHMgY29ycmVzcG9uZGluZyB0byB0aGUgY29sb3JzCmNvbG9yT3JkZXIyID0gYygiZ3JleSIsIHN0YW5kYXJkQ29sb3JzKDUwKSk7Cm1vZHVsZUxhYmVsczIgPSBtYXRjaChtb2R1bGVDb2xvcnMyLCBjb2xvck9yZGVyMiktMTsKZGltKE1FczIpCgpmcmVxLnRhYiA8LSBhcy5kYXRhLmZyYW1lKHRhYmxlKG1vZHVsZUNvbG9yczIpKQoKY29sbmFtZXMoZnJlcS50YWIpIDwtIGMoIk1vZHVsZXMiLCAiTWVtYmVyc2hpcCIpCmZyZXEudGFiID0gZnJlcS50YWJbb3JkZXIoLWZyZXEudGFiJE1lbWJlcnNoaXApLCBdCnJvd25hbWVzKGZyZXEudGFiKSA8LSAxOm5yb3coZnJlcS50YWIpCmZyZXEudGFiJE1vZHVsZXMgPSBmYWN0b3IoZnJlcS50YWIkTW9kdWxlcywgbGV2ZWxzID0gZnJlcS50YWIkTW9kdWxlcykKCmdncGxvdChmcmVxLnRhYiwgYWVzKHggPSBNb2R1bGVzLCB5ID0gTWVtYmVyc2hpcCwgZmlsbCA9IE1vZHVsZXMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsICBjb2xvciA9ICJncmV5MSIsIHNpemUgPSAwLjUpICsKICBsYWJzKHRpdGxlID0gIk1vZHVsZSBTaXplIGluIEhlYWx0aHkgUGF0aWVudHMiLAogICAgICAgeCA9ICJNb2R1bGVzIiwKICAgICAgIHkgPSAiTWVtYmVyc2hpcCIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9YXMuY2hhcmFjdGVyKGZyZXEudGFiJE1vZHVsZXMpKSArICMgU2V0IGJhciBjb2xvcnMKICB0aGVtZV9taW5pbWFsKCkgKyAKICB0aGVtZSgKICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCksICAgICAgICAjIFJlbW92ZSBncmlkIGxpbmVzCiAgICBheGlzLmxpbmUgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuNSksICAjIEFkanVzdCBsaW5lIHdpZHRoIG9mIGF4ZXMKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3I9J2JsYWNrJywgYW5nbGUgPSA5MCksICMgQWRqdXN0IGZvbnQgc2l6ZSBvZiBheGlzIHRleHQKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMCwgY29sb3I9J2JsYWNrJyksCiAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikgIyBBZGp1c3QgZm9udCBzaXplIG9mIGF4aXMgbGFiZWxzCiAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSAgIyBSZW1vdmUgbGVnZW5kIGlmIHVubmVjZXNzYXJ5CmBgYAoKIyMgCgojIEQuIERHQ0EKCmBgYHtyfQojIHJlbnY6Omluc3RhbGwoImFuZHltY2tlbnppZS9ER0NBIikKbGlicmFyeShER0NBKQpgYGAKCiMjIEQuMSBMb3cgdmFyaWFiaWxpdHkgKHA9MC4xKQoKYGBge3J9CmxvZ19tb2R1bGVfZWZmZWN0X21hZ25pdHVkZSA9IDAuMQpkZ2NhX2Rlc2lnbiA8LSBkYXRhLmZyYW1lKAogIEhlYWx0aHkgPSBhcy5udW1lcmljKCFkYXRUcmFpdHMkQ29uZGl0aW9uKSwKICBEaXNlYXNlZCA9IGFzLm51bWVyaWMoZGF0VHJhaXRzJENvbmRpdGlvbikpCnJvd25hbWVzKGRnY2FfZGVzaWduKSA8LSByb3duYW1lcyhkYXRUcmFpdHMpCgpzeW50aGV0aWNfZXhwcmVzc2lvbl9kYXRhID0gcmVhZC5jc3YoInN5bnRoZXRpY19leHByZXNzaW9uX2RhdGFfZWZmZWN0XzAuMS5jc3YiLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gIlgiKQptb2R1bGVzX2xpc3QgPSByZWFkLmNzdigid2djbmEvbW9kdWxlQ29sb3JzX1dHQ05BXzAuMS5jc3YiLCBoZWFkZXI9VCkKCm1vZHVsZURDX3JlcyA8LSBtb2R1bGVEQyhpbnB1dE1hdCA9IHN5bnRoZXRpY19leHByZXNzaW9uX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBhcy5tYXRyaXgoZGdjYV9kZXNpZ24pLAogICAgICAgICAgICAgICAgICAgICAgY29tcGFyZSA9IGMoIkhlYWx0aHkiLCJEaXNlYXNlZCIpLCAjIENvbHVtbiBuYW1lIGluIGRnY2FfZGVzaWduCiAgICAgICAgICAgICAgICAgICAgICBnZW5lcyA9IG1vZHVsZXNfbGlzdCRnZW5lcywKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IG1vZHVsZXNfbGlzdCRtb2R1bGVzLAogICAgICAgICAgICAgICAgICAgICAgblBlcm1zID0gNTAsCiAgICAgICAgICAgICAgICAgICAgICBudW1iZXJfRENfZ2VuZXMgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgIGRDb3JBdmdNZXRob2QgPSAibWVkaWFuIikKCmBgYAoKYGBge3J9Cm1vZHVsZURDX3JlcwpgYGAKCmBgYHtyfQoKIyAxLiBFeHRyYWN0IGFuZCBwcmVwYXJlIGRhdGEKbW9kdWxlX3Jhd19uYW1lcyA8LSBtb2R1bGVEQ19yZXMkTW9kdWxlICMgZS5nLiwgYygiYmx1ZSIsICJyZWQiLCAiZ3JlZW4iKQptb2R1bGVfbWVhbkRDcyA8LSBtb2R1bGVEQ19yZXMkTWVEQyAgICAjIGUuZy4sIGMoMC4xLCAtMC4yLCAwLjA1KQptb2R1bGVfcFZhbHMgPC0gbW9kdWxlRENfcmVzJHBWYWwgICAgICAjIGUuZy4sIGMoMC4wMDEsIDAuMDUsIDAuMSkKCiMgMi4gQ3JlYXRlIHRoZSBkZXNpcmVkIG1vZHVsZSBvcmRlciB3aXRoICJNRSIgcHJlZml4Cm1vZHVsZV9vcmRlciA8LSBwYXN0ZTAoIk1FIiwgbW9kdWxlX3Jhd19uYW1lcykgIyBUaGlzIHdpbGwgYmUgTUVibHVlLCBNRXJlZCwgTUVncmVlbgoKIyAzLiBDcmVhdGUgdGhlIGhlYXRtYXAgbWF0cml4IGFuZCBhc3NpZ24gbWVhbmluZ2Z1bCByb3cgbmFtZXMKZGdjYV9oZWF0bWFwX21hdHJpeCA8LSBhcy5tYXRyaXgobW9kdWxlX21lYW5EQ3MpCiMgQXNzaWduIHRoZSBuZXcgbW9kdWxlIG5hbWVzIGFzIHJvdyBuYW1lcyBmb3IgdGhlIG1hdHJpeC4KIyBUaGlzIGlzIGNydWNpYWwgZm9yIGxhYmVsZWRIZWF0bWFwIHRvIGNvcnJlY3RseSBhc3NvY2lhdGUgbGFiZWxzIHdpdGggcm93cy4Kcm93bmFtZXMoZGdjYV9oZWF0bWFwX21hdHJpeCkgPC0gbW9kdWxlX29yZGVyCmNvbG5hbWVzKGRnY2FfaGVhdG1hcF9tYXRyaXgpIDwtIGMoIk1lYW4gRGlmZi4gQ29yLiIpICMgVGhpcyBjb2x1bW4gbmFtZSB3aWxsIGJlIGZvciB4TGFiZWwKCiMgNC4gUHJlcGFyZSB0ZXh0IG1hdHJpeCBmb3IgcC12YWx1ZXMgYW5kIG1lYW5EQ3MKZGdjYV90ZXh0TWF0cml4ID0gcGFzdGUoc2lnbmlmKGRnY2FfaGVhdG1hcF9tYXRyaXgsIDIpLCAiXG4oIiwKICAgICAgICAgICAgICAgICAgICAgICAgc2lnbmlmKG1vZHVsZV9wVmFscywgMSksICIpIiwgc2VwID0gIiIpOwpkaW0oZGdjYV90ZXh0TWF0cml4KSA9IGRpbShkZ2NhX2hlYXRtYXBfbWF0cml4KSAjIEVuc3VyZSBkaW1lbnNpb25zIGFyZSBjb3JyZWN0bHkgc2V0CgojIFBsb3R0aW5nCmNhdCgiXG4tLS0gREdDQSBQcmUtZGVmaW5lZCBNb2R1bGUtbGV2ZWwgRGlmZmVyZW50aWFsIENvcnJlbGF0aW9uIEhlYXRtYXAgLS0tXG4iKQpzaXplR3JXaW5kb3coNiwgOCkgIyBBZGp1c3Qgd2luZG93IHNpemUgZm9yIHBsb3QKCiMgRGVmaW5lIG91dHB1dCBmaWxlIHBhdGggYW5kIG5hbWUKb3V0cHV0X3BuZ19maWxlIDwtIHBhc3RlMCgiZGNnYS9ER0NBX01vZHVsZV9NZWFuRENfSGVhdG1hcF9FZmZlY3RfIiwgbG9nX21vZHVsZV9lZmZlY3RfbWFnbml0dWRlLCAiLnBuZyIpCgojIEVuc3VyZSB0aGUgJ2RjZ2EnIGRpcmVjdG9yeSBleGlzdHMuIElmIG5vdCwgY3JlYXRlIGl0LgppZiAoIWRpci5leGlzdHMoImRjZ2EiKSkgewogIGRpci5jcmVhdGUoImRjZ2EiKQp9CgojICMgT3BlbiBQTkcgZGV2aWNlIHRvIHNhdmUgdGhlIHBsb3QKIyBwbmcob3V0cHV0X3BuZ19maWxlLCB3aWR0aCA9IDYwMCwgaGVpZ2h0ID0gODAwLCByZXM9MTAwLCBiZyA9ICJ3aGl0ZSIpCiMgCiMgIyBBZGp1c3QgcGxvdCBtYXJnaW5zIChib3R0b20sIGxlZnQsIHRvcCwgcmlnaHQpCiMgIyBJbmNyZWFzZSBsZWZ0IG1hcmdpbiB0byBlbnN1cmUgZW5vdWdoIHNwYWNlIGZvciBsb25nZXIgbW9kdWxlIG5hbWVzCiMgcGFyKG1hciA9IGMoNiwgMTIsIDMsIDMpKQojIAojIGxhYmVsZWRIZWF0bWFwKE1hdHJpeCA9IGRnY2FfaGVhdG1hcF9tYXRyaXgsCiMgICAgICAgICAgICAgICAgeExhYmVscyA9ICJDb25kaXRpb24iLCAgICAgICAgICAgIyBPdmVyYWxsIGxhYmVsIGZvciB0aGUgeC1heGlzCiMgICAgICAgICAgICAgICAgeUxhYmVscyA9IG1vZHVsZV9vcmRlciwgICAgICAgICMgT3ZlcmFsbCBsYWJlbCBmb3IgdGhlIHktYXhpcwojICAgICAgICAgICAgICAgIHlTeW1ib2xzID0gcm93bmFtZXMoZGdjYV9oZWF0bWFwX21hdHJpeCksICMgSW5kaXZpZHVhbCBsYWJlbHMgZm9yIGVhY2ggcm93IChNRWJsdWUsIE1FcmVkLCBldGMuKQojICAgICAgICAgICAgICAgIGNvbG9yTGFiZWxzID0gRkFMU0UsCiMgICAgICAgICAgICAgICAgY29sb3JzID0gYmx1ZVdoaXRlUmVkKDUwKSwgICAgICAgIyBDb2xvciBzY2FsZSAoZnJvbSBXR0NOQSkKIyAgICAgICAgICAgICAgICB0ZXh0TWF0cml4ID0gZGdjYV90ZXh0TWF0cml4LCAgICAjIFRleHQgdG8gZGlzcGxheSBpbnNpZGUgaGVhdG1hcCBjZWxscwojICAgICAgICAgICAgICAgIHNldFN0ZE1hcmdpbnMgPSBGQUxTRSwKIyAgICAgICAgICAgICAgICBjZXgudGV4dCA9IDAuNywgICAgICAgICAgICAgICAgICAjIFNpemUgb2YgdGV4dCBJTlNJREUgdGhlIGNlbGxzIChtZWFuREMgKHAtdmFsKSkKIyAgICAgICAgICAgICAgICBjZXgubGFiID0gMSwgICAgICAgICAgICAgICAgICAgICAjIFNpemUgb2YgIkNvbmRpdGlvbiIgYW5kICJNb2R1bGUgTmFtZXMiIG92ZXJhbGwgbGFiZWxzCiMgICAgICAgICAgICAgICAgY2V4LnJvd0xhYmVscyA9IDAuOSwgICAgICAgICAgICAgIyAqKkNSSVRJQ0FMKio6IFNpemUgb2YgdGhlIGluZGl2aWR1YWwgcm93IGxhYmVscyAoeVN5bWJvbHMpLiBBZGp1c3QgYXMgbmVlZGVkLgojICAgICAgICAgICAgICAgIHpsaW0gPSBjKC1tYXgoYWJzKGRnY2FfaGVhdG1hcF9tYXRyaXgpKSwgbWF4KGFicyhkZ2NhX2hlYXRtYXBfbWF0cml4KSkpLCAjIFN5bW1ldHJpYyBjb2xvciBzY2FsZQojICAgICAgICAgICAgICAgIG1haW4gPSBwYXN0ZSgiREdDQTogTWVkaWFuIFdpdGhpbi1Nb2R1bGUgRGlmZiIpKSAjIE1haW4gcGxvdCB0aXRsZQojIAoKCiMgUGxvdHRpbmcgdGhlIGhlYXRtYXAgdXNpbmcgbGFiZWxlZEhlYXRtYXAKIyBDdXN0b21pemUgdGV4dCBtYXRyaXggZm9yIGNvcnJlbGF0aW9uIGFuZCBwLXZhbHVlIGRpc3BsYXkKZGV2Lm9mZigpCnBuZyhvdXRwdXRfcG5nX2ZpbGUsd2lkdGg9NixoZWlnaHQ9NSx1bml0cz0iaW4iLHJlcz02MDApCnRleHRNYXRyaXggPSBwYXN0ZShzaWduaWYobW9kdWxlVHJhaXRDb3IsIDIpLCAiXG4oIiwKICAgICAgICAgICAgICAgICAgICBzaWduaWYobW9kdWxlVHJhaXRQdmFsdWUsIDEpLCAiKSIsIHNlcCA9ICIiKTsKICAgICAgICAgICAgICAgICAgICBkaW0odGV4dE1hdHJpeCkgPSBkaW0obW9kdWxlVHJhaXRDb3IpCiAgICAgICAgICAgICAgICAgICAgcGFyKG1hciA9IGMoNiwgMTAsIDMsIDMpKQpwYXIobWFyID0gYyg4LCA4LCAyLCAxKSkgICMgUmVkdWNlIG1hcmdpbnMgKGJvdHRvbSwgbGVmdCwgdG9wLCByaWdodCkKbGFiZWxlZEhlYXRtYXAoTWF0cml4ID0gZGdjYV9oZWF0bWFwX21hdHJpeCwKICAgICAgICAgICAgICAgeExhYmVscyA9ICJDb25kaXRpb24iLAogICAgICAgICAgICAgICB5TGFiZWxzID0gbW9kdWxlX29yZGVyLAogICAgICAgICAgICAgICB5U3ltYm9scyA9IHJvd25hbWVzKGRnY2FfaGVhdG1hcF9tYXRyaXgpLAogICAgICAgICAgICAgICBjb2xvckxhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICBjb2xvcnMgPSBibHVlV2hpdGVSZWQoNTApLCAjIFJlZCBmb3IgcG9zaXRpdmUsIGJsdWUgZm9yIG5lZ2F0aXZlIGNvcnJlbGF0aW9uCiAgICAgICAgICAgICAgIHRleHRNYXRyaXggPSBkZ2NhX3RleHRNYXRyaXgsCiAgICAgICAgICAgICAgIHNldFN0ZE1hcmdpbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgY2V4LnRleHQgPSAwLjUsICMgQWRqdXN0IHRleHQgc2l6ZQogICAgICAgICAgICAgICAjIHpsaW0gPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgbWFpbiA9IHBhc3RlKCJER0NBIikKICAgICAgICAgICAgICAgKQoKY2F0KCJcbk1vZHVsZS1UcmFpdCBSZWxhdGlvbnNoaXAgSGVhdG1hcCBnZW5lcmF0ZWQuXG4iKQpkZXYub2ZmKCkKY2F0KHNwcmludGYoIlNhdmVkICVzXG5cbiIsIG91dHB1dF9wbmdfZmlsZSkpCmBgYAoKIyMgRC4yIEhpZ2ggVmFyaWFiaWxpdHkgKHA9MC45KQoKYGBge3J9CmxvZ19tb2R1bGVfZWZmZWN0X21hZ25pdHVkZSA9IDAuOQpkZ2NhX2Rlc2lnbiA8LSBkYXRhLmZyYW1lKAogIEhlYWx0aHkgPSBhcy5udW1lcmljKCFkYXRUcmFpdHMkQ29uZGl0aW9uKSwKICBEaXNlYXNlZCA9IGFzLm51bWVyaWMoZGF0VHJhaXRzJENvbmRpdGlvbikpCnJvd25hbWVzKGRnY2FfZGVzaWduKSA8LSByb3duYW1lcyhkYXRUcmFpdHMpCgpzeW50aGV0aWNfZXhwcmVzc2lvbl9kYXRhID0gcmVhZC5jc3YoInN5bnRoZXRpY19leHByZXNzaW9uX2RhdGFfZWZmZWN0XzAuOS5jc3YiLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gIlgiKQptb2R1bGVzX2xpc3QgPSByZWFkLmNzdigid2djbmEvbW9kdWxlQ29sb3JzX1dHQ05BXzAuOS5jc3YiLCBoZWFkZXI9VCkKCm1vZHVsZURDX3JlcyA8LSBtb2R1bGVEQyhpbnB1dE1hdCA9IHN5bnRoZXRpY19leHByZXNzaW9uX2RhdGEsCiAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBhcy5tYXRyaXgoZGdjYV9kZXNpZ24pLAogICAgICAgICAgICAgICAgICAgICAgY29tcGFyZSA9IGMoIkhlYWx0aHkiLCJEaXNlYXNlZCIpLCAjIENvbHVtbiBuYW1lIGluIGRnY2FfZGVzaWduCiAgICAgICAgICAgICAgICAgICAgICBnZW5lcyA9IG1vZHVsZXNfbGlzdCRnZW5lcywKICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IG1vZHVsZXNfbGlzdCRtb2R1bGVzLAogICAgICAgICAgICAgICAgICAgICAgblBlcm1zID0gNTAsCiAgICAgICAgICAgICAgICAgICAgICBudW1iZXJfRENfZ2VuZXMgPSAxMCwKICAgICAgICAgICAgICAgICAgICAgIGRDb3JBdmdNZXRob2QgPSAibWVkaWFuIikKCmBgYAoKYGBge3J9Cm1vZHVsZURDX3JlcwpgYGAKCmBgYHtyfQojIDEuIEV4dHJhY3QgYW5kIHByZXBhcmUgZGF0YQptb2R1bGVfcmF3X25hbWVzIDwtIG1vZHVsZURDX3JlcyRNb2R1bGUgIyBlLmcuLCBjKCJibHVlIiwgInJlZCIsICJncmVlbiIpCm1vZHVsZV9tZWFuRENzIDwtIG1vZHVsZURDX3JlcyRNZURDICAgICMgZS5nLiwgYygwLjEsIC0wLjIsIDAuMDUpCm1vZHVsZV9wVmFscyA8LSBtb2R1bGVEQ19yZXMkcFZhbCAgICAgICMgZS5nLiwgYygwLjAwMSwgMC4wNSwgMC4xKQoKIyAyLiBDcmVhdGUgdGhlIGRlc2lyZWQgbW9kdWxlIG9yZGVyIHdpdGggIk1FIiBwcmVmaXgKbW9kdWxlX29yZGVyIDwtIHBhc3RlMCgiTUUiLCBtb2R1bGVfcmF3X25hbWVzKSAjIFRoaXMgd2lsbCBiZSBNRWJsdWUsIE1FcmVkLCBNRWdyZWVuCgojIDMuIENyZWF0ZSB0aGUgaGVhdG1hcCBtYXRyaXggYW5kIGFzc2lnbiBtZWFuaW5nZnVsIHJvdyBuYW1lcwpkZ2NhX2hlYXRtYXBfbWF0cml4IDwtIGFzLm1hdHJpeChtb2R1bGVfbWVhbkRDcykKIyBBc3NpZ24gdGhlIG5ldyBtb2R1bGUgbmFtZXMgYXMgcm93IG5hbWVzIGZvciB0aGUgbWF0cml4LgojIFRoaXMgaXMgY3J1Y2lhbCBmb3IgbGFiZWxlZEhlYXRtYXAgdG8gY29ycmVjdGx5IGFzc29jaWF0ZSBsYWJlbHMgd2l0aCByb3dzLgpyb3duYW1lcyhkZ2NhX2hlYXRtYXBfbWF0cml4KSA8LSBtb2R1bGVfb3JkZXIKY29sbmFtZXMoZGdjYV9oZWF0bWFwX21hdHJpeCkgPC0gYygiTWVhbiBEaWZmLiBDb3IuIikgIyBUaGlzIGNvbHVtbiBuYW1lIHdpbGwgYmUgZm9yIHhMYWJlbAoKIyA0LiBQcmVwYXJlIHRleHQgbWF0cml4IGZvciBwLXZhbHVlcyBhbmQgbWVhbkRDcwpkZ2NhX3RleHRNYXRyaXggPSBwYXN0ZShzaWduaWYoZGdjYV9oZWF0bWFwX21hdHJpeCwgMiksICJcbigiLAogICAgICAgICAgICAgICAgICAgICAgICBzaWduaWYobW9kdWxlX3BWYWxzLCAxKSwgIikiLCBzZXAgPSAiIik7CmRpbShkZ2NhX3RleHRNYXRyaXgpID0gZGltKGRnY2FfaGVhdG1hcF9tYXRyaXgpICMgRW5zdXJlIGRpbWVuc2lvbnMgYXJlIGNvcnJlY3RseSBzZXQKCiMgUGxvdHRpbmcKY2F0KCJcbi0tLSBER0NBIFByZS1kZWZpbmVkIE1vZHVsZS1sZXZlbCBEaWZmZXJlbnRpYWwgQ29ycmVsYXRpb24gSGVhdG1hcCAtLS1cbiIpCnNpemVHcldpbmRvdyg2LCA4KSAjIEFkanVzdCB3aW5kb3cgc2l6ZSBmb3IgcGxvdAoKIyBEZWZpbmUgb3V0cHV0IGZpbGUgcGF0aCBhbmQgbmFtZQpvdXRwdXRfcG5nX2ZpbGUgPC0gcGFzdGUwKCJkY2dhL0RHQ0FfTW9kdWxlX01lYW5EQ19IZWF0bWFwX0VmZmVjdF8iLCBsb2dfbW9kdWxlX2VmZmVjdF9tYWduaXR1ZGUsICIucG5nIikKCiMgRW5zdXJlIHRoZSAnZGNnYScgZGlyZWN0b3J5IGV4aXN0cy4gSWYgbm90LCBjcmVhdGUgaXQuCmlmICghZGlyLmV4aXN0cygiZGNnYSIpKSB7CiAgZGlyLmNyZWF0ZSgiZGNnYSIpCn0KCmRldi5vZmYoKQpwbmcob3V0cHV0X3BuZ19maWxlLHdpZHRoPTYsaGVpZ2h0PTUsdW5pdHM9ImluIixyZXM9NjAwKQp0ZXh0TWF0cml4ID0gcGFzdGUoc2lnbmlmKG1vZHVsZVRyYWl0Q29yLCAyKSwgIlxuKCIsCiAgICAgICAgICAgICAgICAgICAgc2lnbmlmKG1vZHVsZVRyYWl0UHZhbHVlLCAxKSwgIikiLCBzZXAgPSAiIik7CiAgICAgICAgICAgICAgICAgICAgZGltKHRleHRNYXRyaXgpID0gZGltKG1vZHVsZVRyYWl0Q29yKQogICAgICAgICAgICAgICAgICAgIHBhcihtYXIgPSBjKDYsIDEwLCAzLCAzKSkKcGFyKG1hciA9IGMoOCwgOCwgMiwgMSkpICAjIFJlZHVjZSBtYXJnaW5zIChib3R0b20sIGxlZnQsIHRvcCwgcmlnaHQpCmxhYmVsZWRIZWF0bWFwKE1hdHJpeCA9IGRnY2FfaGVhdG1hcF9tYXRyaXgsCiAgICAgICAgICAgICAgIHhMYWJlbHMgPSAiQ29uZGl0aW9uIiwKICAgICAgICAgICAgICAgeUxhYmVscyA9IG1vZHVsZV9vcmRlciwKICAgICAgICAgICAgICAgeVN5bWJvbHMgPSByb3duYW1lcyhkZ2NhX2hlYXRtYXBfbWF0cml4KSwKICAgICAgICAgICAgICAgY29sb3JMYWJlbHMgPSBGQUxTRSwKICAgICAgICAgICAgICAgY29sb3JzID0gYmx1ZVdoaXRlUmVkKDUwKSwgIyBSZWQgZm9yIHBvc2l0aXZlLCBibHVlIGZvciBuZWdhdGl2ZSBjb3JyZWxhdGlvbgogICAgICAgICAgICAgICB0ZXh0TWF0cml4ID0gZGdjYV90ZXh0TWF0cml4LAogICAgICAgICAgICAgICBzZXRTdGRNYXJnaW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgIGNleC50ZXh0ID0gMC41LCAjIEFkanVzdCB0ZXh0IHNpemUKICAgICAgICAgICAgICAgIyB6bGltID0gYygtMSwgMSksCiAgICAgICAgICAgICAgIG1haW4gPSBwYXN0ZSgiREdDQSIpCiAgICAgICAgICAgICAgICkKCmNhdCgiXG5Nb2R1bGUtVHJhaXQgUmVsYXRpb25zaGlwIEhlYXRtYXAgZ2VuZXJhdGVkLlxuIikKZGV2Lm9mZigpCmNhdChzcHJpbnRmKCJTYXZlZCAlc1xuXG4iLCBvdXRwdXRfcG5nX2ZpbGUpKQpgYGAKCiMgRS4gRGlmZkNvRXhwCgpgYGB7cn0KIyByZW52OjppbnN0YWxsKCJkaWZmY29leHAiKQojIHJlbnY6Omluc3RhbGwoIkdFT3F1ZXJ5IikKbGlicmFyeShkaWZmY29leHApCmFsbG93V0dDTkFUaHJlYWRzKCkKYGBgCgojIyBFLjEgTG93IHZhcmlhYmlsaXR5IChwPTAuMSkKCmBgYHtyfQpleHBycy5oID0gcmVhZC5jc3YoImRjb3ItbWV0aG9kL3N5bnRoZXRpY19oZWFsdGh5XzAuMS50eHQiLCBzZXAgPSAiXHQiLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gIkdlbmVzIikKZXhwcnMuZCA9IHJlYWQuY3N2KCJkY29yLW1ldGhvZC9zeW50aGV0aWNfZGlzZWFzZWRfMC4xLnR4dCIsIHNlcCA9ICJcdCIsIGhlYWRlcj1ULCByb3cubmFtZXMgPSAiR2VuZXMiKQphbGxvd1dHQ05BVGhyZWFkcygpCgojIERpc2Vhc2VkIC0gSGVhbHRoeTsgaGlnaCBjb3JyZWxhdGlvbiAtLT4gZGlzZWFzZWQKIyBTdGVwIDEKcmVzLmxvdyA9ZGlmZmNvZXhwKGV4cHJzLjEgPSBleHBycy5kLCBleHBycy4yID0gZXhwcnMuaCwKICAgICAgICAgICAgICAgICAgIHIubWV0aG9kID0gInBlYXJzb24iLHEubWV0aG9kID0gImJvbmZlcnJvbmkiKQpgYGAKCmBgYHtyfQpsaWJyYXJ5KFdHQ05BKQojIFdHQ05BIG9mdGVuIHJlY29tbWVuZHMgc2V0dGluZyB0aGlzCm9wdGlvbnMoc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQphbGxvd1dHQ05BVGhyZWFkcygpICMgVXNlIG11bHRpcGxlIGNvcmVzIGlmIGF2YWlsYWJsZQoKIyAtLS0gSW5wdXQgRGF0YSAtLS0KIyBSZWFkIGV4cHJlc3Npb24gZGF0YSBmb3IgdHdvIGNvbmRpdGlvbnMKIyBFbnN1cmUgJ3Jvdy5uYW1lcyA9ICJHZW5lcyInIGNvcnJlY3RseSBpZGVudGlmaWVzIHRoZSBnZW5lIElEIGNvbHVtbgpleHBycy4xID0gcmVhZC5jc3YoImRjb3ItbWV0aG9kL3N5bnRoZXRpY19oZWFsdGh5XzAuMS50eHQiLCBzZXAgPSAiXHQiLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gIkdlbmVzIikKZXhwcnMuMiA9IHJlYWQuY3N2KCJkY29yLW1ldGhvZC9zeW50aGV0aWNfZGlzZWFzZWRfMC4xLnR4dCIsIHNlcCA9ICJcdCIsIGhlYWRlcj1ULCByb3cubmFtZXMgPSAiR2VuZXMiKQoKIyBFbnN1cmUgZ2VuZSBvcmRlciBpcyBjb25zaXN0ZW50IGFjcm9zcyBjb25kaXRpb25zCmNvbW1vbkdlbmVzIDwtIGludGVyc2VjdChyb3duYW1lcyhleHBycy4xKSwgcm93bmFtZXMoZXhwcnMuMikpCmV4cHJzLjEgPC0gZXhwcnMuMVtjb21tb25HZW5lcywgXQpleHBycy4yIDwtIGV4cHJzLjJbY29tbW9uR2VuZXMsIF0KCiMgSU1QT1JUQU5UIGZvciBXR0NOQTogVHJhbnNwb3NlIGRhdGEgc28gc2FtcGxlcyBhcmUgcm93cyBhbmQgZ2VuZXMgYXJlIGNvbHVtbnMKZGF0RXhwcjEgPC0gYXMuZGF0YS5mcmFtZSh0KGV4cHJzLjEpKQpkYXRFeHByMiA8LSBhcy5kYXRhLmZyYW1lKHQoZXhwcnMuMikpCgojIENvbWJpbmUgZXhwcmVzc2lvbiBkYXRhIGZvciBtb2R1bGUgZWlnZW5nZW5lIGNhbGN1bGF0aW9uIGFuZCB0cmFpdCBtYXBwaW5nCiMgVGhlIGNvbHVtbiBuYW1lcyAoZ2VuZXMpIG11c3QgYmUgaWRlbnRpY2FsIGZvciBjYmluZCB0byB3b3JrIGNvcnJlY3RseQpkYXRFeHByX2NvbWJpbmVkIDwtIHQoY2JpbmQoZXhwcnMuMSwgZXhwcnMuMikpCgpjYXQoIkRhdGEgZGltZW5zaW9ucyBmb3IgV0dDTkEgKHNhbXBsZXMgeCBnZW5lcyk6XG4iKQpjYXQoIkNvbmRpdGlvbiAxOiIsIGRpbShkYXRFeHByMSksICJcbiIpCmNhdCgiQ29uZGl0aW9uIDI6IiwgZGltKGRhdEV4cHIyKSwgIlxuIikKY2F0KCJDb21iaW5lZCBkYXRhOiIsIGRpbShkYXRFeHByX2NvbWJpbmVkKSwgIlxuXG4iKQoKIyAtLS0gRGVmaW5lIFNvZnQtdGhyZXNob2xkaW5nIFBvd2VyIChiZXRhKSAtLS0KIyBBcyBwZXIgdGhlIHBhcGVyLCBiZXRhIGlzIGEgdHVuaW5nIHBhcmFtZXRlciBmb3IgdGhlIGFkamFjZW5jeSBkaWZmZXJlbmNlIG1hdHJpeC4KIyBJdHMgY2hvaWNlIGltcGFjdHMgdGhlIHN0cmluZ2VuY3kgb2YgZGlmZmVyZW50aWFsIGNvcnJlbGF0aW9uLgpiZXRhID0gMSAjIEV4YW1wbGUgdmFsdWUsIGFkanVzdCBhcyBuZWVkZWQgYmFzZWQgb24geW91ciBkYXRhIGFuZCBkZXNpcmVkIHN0cmluZ2VuY3kKCgojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQojIERpZmZDb0V4IEFsZ29yaXRobSBTdGVwcwojID09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PQoKIyAtLS0gU3RlcCAxOiBCdWlsZCBjb3JyZWxhdGlvbiBtYXRyaXggQ1trXSB3aXRoaW4gZWFjaCBjb25kaXRpb24gayAtLS0KY2F0KCJTdGVwIDE6IENvbXB1dGluZyBjb3JyZWxhdGlvbiBtYXRyaWNlcyBmb3IgZWFjaCBjb25kaXRpb24uLi5cbiIpCmNvck1hdHJpeDEgPC0gV0dDTkE6OmNvcihkYXRFeHByMSwgbWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCmNvck1hdHJpeDIgPC0gV0dDTkE6OmNvcihkYXRFeHByMiwgbWV0aG9kID0gInNwZWFybWFuIiwgdXNlID0gInBhaXJ3aXNlLmNvbXBsZXRlLm9icyIpCgpjYXQoIkNvcnJlbGF0aW9uIG1hdHJpY2VzIGNvbXB1dGVkLlxuXG4iKQoKIyAtLS0gU3RlcCAyOiBDb21wdXRlIG1hdHJpeCBvZiBhZGphY2VuY3kgZGlmZmVyZW5jZSBEIC0tLQojIGRfaWogPSB8c2lnbihjb3JfaWpeKDEpKShjb3JfaWpeKDEpKV4yIC0gc2lnbihjb3JfaWpeKDIpKShjb3JfaWpeKDIpKV4yfF5iZXRhCmNhdCgiU3RlcCAyOiBDb21wdXRpbmcgdGhlIGFkamFjZW5jeSBkaWZmZXJlbmNlIG1hdHJpeCBELi4uXG4iKQoKIyBHZXQgYWxsIHVuaXF1ZSBnZW5lcyAoc2hvdWxkIGJlIGNvbW1vbkdlbmVzIGZyb20gYWJvdmUpCmFsbEdlbmVzIDwtIGNvbG5hbWVzKGRhdEV4cHIxKQoKIyBJbml0aWFsaXplIEQgbWF0cml4IHdpdGggemVyb3MKRF9tYXRyaXggPC0gbWF0cml4KDAsCiAgICAgICAgICAgICAgICAgICBucm93ID0gbGVuZ3RoKGFsbEdlbmVzKSwKICAgICAgICAgICAgICAgICAgIG5jb2wgPSBsZW5ndGgoYWxsR2VuZXMpLAogICAgICAgICAgICAgICAgICAgZGltbmFtZXMgPSBsaXN0KGFsbEdlbmVzLCBhbGxHZW5lcykpCgojIExvb3AgdGhyb3VnaCBhbGwgZ2VuZSBwYWlycyB0byBjYWxjdWxhdGUgRF9pagpmb3IgKGkgaW4gMTpsZW5ndGgoYWxsR2VuZXMpKSB7CiAgZm9yIChqIGluIGk6bGVuZ3RoKGFsbEdlbmVzKSkgeyAjIExvb3AgaiBmcm9tIGkgZm9yIHVwcGVyIHRyaWFuZ2xlIChhbmQgc3ltbWV0cnkpCiAgICBnZW5lX2kgPC0gYWxsR2VuZXNbaV0KICAgIGdlbmVfaiA8LSBhbGxHZW5lc1tqXQoKICAgICMgR2V0IGNvcnJlbGF0aW9ucyBmb3IgdGhlIHBhaXIKICAgIGNvcl9pal8xIDwtIGNvck1hdHJpeDFbZ2VuZV9pLCBnZW5lX2pdCiAgICBjb3JfaWpfMiA8LSBjb3JNYXRyaXgyW2dlbmVfaSwgZ2VuZV9qXQoKICAgICMgSGFuZGxlIHBvdGVudGlhbCBOQSBjb3JyZWxhdGlvbnMKICAgICMgSWYgYSBjb3JyZWxhdGlvbiBpcyBOQSBpbiBlaXRoZXIgY29uZGl0aW9uLCBzZXQgZGlmZmVyZW50aWFsIHRvIDAgZm9yIHRoYXQgcGFpcgogICAgaWYgKGlzLm5hKGNvcl9pal8xKSB8fCBpcy5uYShjb3JfaWpfMikpIHsKICAgICAgRF9tYXRyaXhbZ2VuZV9pLCBnZW5lX2pdIDwtIDAKICAgICAgRF9tYXRyaXhbZ2VuZV9qLCBnZW5lX2ldIDwtIDAKICAgICAgbmV4dAogICAgfQoKICAgICMgQ2FsY3VsYXRlIHNpZ25lZCBzcXVhcmVkIGNvcnJlbGF0aW9ucwogICAgc19pal8xIDwtIHNpZ24oY29yX2lqXzEpICogKGNvcl9pal8xXjIpCiAgICBzX2lqXzIgPC0gc2lnbihjb3JfaWpfMikgKiAoY29yX2lqXzJeMikKCiAgICAjIENhbGN1bGF0ZSBkX2lqIGFzIHBlciBwYXBlcidzIGZvcm11bGE6IHxzX2lqXzEgLSBzX2lqXzJ8XmJldGEKICAgIGRfaWpfdmFsIDwtIGFicyhzX2lqXzEgLSBzX2lqXzIpXmJldGEKCiAgICAjIFBvcHVsYXRlIERfbWF0cml4IHN5bW1ldHJpY2FsbHkKICAgIERfbWF0cml4W2dlbmVfaSwgZ2VuZV9qXSA8LSBkX2lqX3ZhbAogICAgRF9tYXRyaXhbZ2VuZV9qLCBnZW5lX2ldIDwtIGRfaWpfdmFsICMgRW5zdXJlIHN5bW1ldHJ5CiAgfQp9CgpjYXQoIkFkamFjZW5jeSBkaWZmZXJlbmNlIG1hdHJpeCBEIGNvbXB1dGVkLiBJdHMgcmFuZ2UgaXMgWyIsIG1pbihEX21hdHJpeCksICIsICIsIG1heChEX21hdHJpeCksICJdXG5cbiIpCgojIC0tLSBTdGVwIDM6IERlcml2ZSB0aGUgVG9wb2xvZ2ljYWwgT3ZlcmxhcCAoVE8pIGJhc2VkIGRpc3NpbWlsYXJpdHkgbWF0cml4IFQgZnJvbSBEIC0tLQpjYXQoIlN0ZXAgMzogQ29tcHV0aW5nIFRvcG9sb2dpY2FsIE92ZXJsYXAgRGlzc2ltaWxhcml0eSAoVE9NKSBmcm9tIEQuLi5cbiIpCgojIEl0J3MgZ29vZCBwcmFjdGljZSB0byBlbnN1cmUgdGhlIGRpYWdvbmFsIGlzIDEgZm9yIGFkamFjZW5jeSBtYXRyaWNlcyBiZWZvcmUgVE9NCmRpYWcoRF9tYXRyaXgpIDwtIDEgIyBHZW5lIGlzIHBlcmZlY3RseSAiZGlmZmVyZW50aWFsbHkgY29leHByZXNzZWQiIHdpdGggaXRzZWxmIGNvbmNlcHR1YWxseQoKIyBVc2UgV0dDTkEncyBUT01zaW1pbGFyaXR5IGZ1bmN0aW9uIG9uIG91ciBjdXN0b20gZGlmZmVyZW50aWFsIGFkamFjZW5jeSBtYXRyaXggRC4KIyBUT01UeXBlPSJ1bnNpZ25lZCIgaXMgZ2VuZXJhbGx5IGFwcHJvcHJpYXRlIGZvciBEX21hdHJpeCB2YWx1ZXMgd2hpY2ggYXJlIFswLDFdLgpUT01fbWF0cml4IDwtIFRPTXNpbWlsYXJpdHkoRF9tYXRyaXgsIFRPTVR5cGUgPSAidW5zaWduZWQiKQoKIyBDb252ZXJ0IFRPTSB0byBkaXNzaW1pbGFyaXR5ICgxIC0gVE9NKQpUX2Rpc3NpbWlsYXJpdHkgPC0gMSAtIFRPTV9tYXRyaXgKCmNhdCgiVG9wb2xvZ2ljYWwgT3ZlcmxhcCBEaXNzaW1pbGFyaXR5IG1hdHJpeCBUIGNvbXB1dGVkLlxuXG4iKQoKIyAtLS0gU3RlcCA0OiBDbHVzdGVyaW5nIGFuZCBNb2R1bGUgSWRlbnRpZmljYXRpb24gLS0tCmNhdCgiU3RlcCA0OiBQZXJmb3JtaW5nIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIGFuZCBtb2R1bGUgaWRlbnRpZmljYXRpb24uLi5cbiIpCgojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nCmdlbmVUcmVlX2RpZmZjb2V4IDwtIGhjbHVzdChhcy5kaXN0KFRfZGlzc2ltaWxhcml0eSksIG1ldGhvZCA9ICJhdmVyYWdlIikKCiMgUGxvdCB0aGUgZGVuZHJvZ3JhbQpwbG90KGdlbmVUcmVlX2RpZmZjb2V4LCB4bGFiID0gIiIsIHN1YiA9ICIiLCBtYWluID0gIkdlbmUgZGVuZHJvZ3JhbSBiYXNlZCBvbiBkaWZmZXJlbnRpYWwgVE9NIiwKICAgICBsYWJlbHMgPSBGQUxTRSwgaGFuZyA9IDAuMDQpCgojIE1vZHVsZSBpZGVudGlmaWNhdGlvbiB1c2luZyBkeW5hbWljVHJlZUN1dAptaW5Nb2R1bGVTaXplX2RpZmZjb2V4IDwtIDEwICMgQWRqdXN0IGJhc2VkIG9uIHlvdXIgZGF0YSBhbmQgZGVzaXJlZCBtb2R1bGUgc2l6ZQpkeW5hbWljTW9kc19kaWZmY29leCA8LSBjdXRyZWVEeW5hbWljKGRlbmRybyA9IGdlbmVUcmVlX2RpZmZjb2V4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RNID0gVF9kaXNzaW1pbGFyaXR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlZXBTcGxpdCA9IDIsICMgQWRqdXN0IGRlZXBTcGxpdCBmb3IgZ3JhbnVsYXJpdHkgKDAtNCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYW1SZXNwZWN0c0RlbmRybyA9IEZBTFNFLCAjIFNldCB0byBUUlVFIGZvciBQQU0gcmVmaW5lbWVudAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbkNsdXN0ZXJTaXplID0gbWluTW9kdWxlU2l6ZV9kaWZmY29leCkKCiMgQXNzaWduIG1vZHVsZSBjb2xvcnMgKGZvciB2aXN1YWxpemF0aW9uIGFuZCBmdXJ0aGVyIGFuYWx5c2lzKQptb2R1bGVDb2xvcnNfZGlmZmNvZXggPC0gbGFiZWxzMmNvbG9ycyhkeW5hbWljTW9kc19kaWZmY29leCkKCmNhdCgiTW9kdWxlIGlkZW50aWZpY2F0aW9uIGNvbXBsZXRlLlxuIikKY2F0KCJOdW1iZXIgb2YgZGlmZmVyZW50aWFsIGNvLWV4cHJlc3Npb24gbW9kdWxlcyBmb3VuZDoiLCBsZW5ndGgodW5pcXVlKG1vZHVsZUNvbG9yc19kaWZmY29leCkpIC0gMSwKICAgICIgKGV4Y2x1ZGluZyBncmV5IGZvciB1bmFzc2lnbmVkIGdlbmVzKVxuIikgIyAtMSBmb3IgJ2dyZXknIG1vZHVsZQpjYXQoIlNpemVzIG9mIG1vZHVsZXM6XG4iKQpwcmludCh0YWJsZShtb2R1bGVDb2xvcnNfZGlmZmNvZXgpKQoKIyBPdXRwdXQgdGhlIG1vZHVsZSBhc3NpZ25tZW50cyB0byBhIGRhdGEgZnJhbWUKZGlmZmVyZW50aWFsX21vZHVsZXMgPC0gZGF0YS5mcmFtZSgKICBHZW5lID0gY29sbmFtZXMoZGF0RXhwcjEpLAogIE1vZHVsZSA9IG1vZHVsZUNvbG9yc19kaWZmY29leAopCgojIFNhdmUgdGhlIGRhdGFmcmFtZSB0byBhIENTViBmaWxlCm91dHB1dF9maWxlIDwtICJkaWZmY29leHAvbW9kdWxlQ29sb3JzX3N5bnRoZXRpY18wLjEuY3N2Igp3cml0ZS5jc3YoZGlmZmVyZW50aWFsX21vZHVsZXMsIGZpbGUgPSBvdXRwdXRfZmlsZSwgcm93Lm5hbWVzID0gRikKY2F0KHBhc3RlMCgiXG5Nb2R1bGUgYXNzaWdubWVudHMgc2F2ZWQgdG8gIiwgb3V0cHV0X2ZpbGUsICJcbiIpKQoKIyAtLS0gTmV3IFNlY3Rpb246IFBsb3R0aW5nIE1vZHVsZS1UcmFpdCBSZWxhdGlvbnNoaXBzIC0tLQoKY2F0KCJcbi0tLSBQbG90dGluZyBNb2R1bGUtVHJhaXQgUmVsYXRpb25zaGlwcyAtLS1cbiIpCgojIENyZWF0ZSBhIHRyYWl0IGRhdGEgZnJhbWUgZm9yIHlvdXIgc2FtcGxlcwojIFRoZSByb3cgbmFtZXMgb2YgdGhlIHRyYWl0IGRhdGEgZnJhbWUgbXVzdCBtYXRjaCB0aGUgc2FtcGxlIG5hbWVzIGluIGRhdEV4cHJfY29tYmluZWQKdHJhaXRfZGF0YSA8LSBkYXRhLmZyYW1lKAogIFNhbXBsZSA9IGMocm93bmFtZXMoZGF0RXhwcjEpLCByb3duYW1lcyhkYXRFeHByMikpLAogIENvbmRpdGlvbiA9IGMocmVwKCJIZWFsdGh5IiwgbnJvdyhkYXRFeHByMSkpLCByZXAoIkRpc2Vhc2VkIiwgbnJvdyhkYXRFeHByMikpKQopCnJvd25hbWVzKHRyYWl0X2RhdGEpIDwtIHRyYWl0X2RhdGEkU2FtcGxlICMgU2V0IHNhbXBsZSBuYW1lcyBhcyByb3cgbmFtZXMKCiMgQ29udmVydCBjYXRlZ29yaWNhbCB0cmFpdCB0byBudW1lcmljIGZvciBjb3JyZWxhdGlvbiAoV0dDTkEgZXhwZWN0cyBudW1lcmljKQojIDAgZm9yIEhlYWx0aHksIDEgZm9yIERpc2Vhc2VkIChhcmJpdHJhcnkgYXNzaWdubWVudCkKdHJhaXRfbnVtZXJpYyA8LSBhcy5kYXRhLmZyYW1lKGFzLm51bWVyaWModHJhaXRfZGF0YSRDb25kaXRpb24gPT0gIkRpc2Vhc2VkIikpCnJvd25hbWVzKHRyYWl0X251bWVyaWMpIDwtIHJvd25hbWVzKHRyYWl0X2RhdGEpCmNvbG5hbWVzKHRyYWl0X251bWVyaWMpIDwtICJEaXNlYXNlZF92c19IZWFsdGh5IgoKY2F0KCJUcmFpdCBkYXRhIHByZXBhcmVkLlxuIikKcHJpbnQoaGVhZCh0cmFpdF9udW1lcmljKSkKY2F0KCJcbiIpCgojIENhbGN1bGF0ZSBNb2R1bGUgRWlnZW5nZW5lcyAoTUVzKQojIE1FcyByZXByZXNlbnQgdGhlICJhdmVyYWdlIiBleHByZXNzaW9uIHByb2ZpbGUgb2YgZWFjaCBtb2R1bGUuCiMgVXNlIHRoZSBjb21iaW5lZCBleHByZXNzaW9uIGRhdGEgYW5kIHRoZSBpZGVudGlmaWVkIG1vZHVsZSBjb2xvcnMuCk1Fc19kaWZmY29leCA8LSBtb2R1bGVFaWdlbmdlbmVzKGRhdEV4cHJfY29tYmluZWQsIG1vZHVsZUNvbG9yc19kaWZmY29leCkkZWlnZW5nZW5lcwpjYXQoIk1vZHVsZSBFaWdlbmdlbmVzIGNhbGN1bGF0ZWQuXG4iKQpwcmludChoZWFkKE1Fc19kaWZmY29leCkpCmNhdCgiXG4iKQoKIyBSZWxhdGUgTUVzIHRvIHRoZSB0cmFpdCAoQ29uZGl0aW9uKQojIENhbGN1bGF0ZSBjb3JyZWxhdGlvbnMgYW5kIHRoZWlyIHAtdmFsdWVzCm1vZHVsZVRyYWl0Q29yIDwtIFdHQ05BOjpjb3IoTUVzX2RpZmZjb2V4LCB0cmFpdF9udW1lcmljLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIiwgbWV0aG9kID0gInBlYXJzb24iKSAjIFBlYXJzb24gaXMgY29tbW9uIGZvciBNRXMKbW9kdWxlVHJhaXRQdmFsdWUgPC0gV0dDTkE6OmNvclB2YWx1ZVN0dWRlbnQobW9kdWxlVHJhaXRDb3IsIG5yb3coZGF0RXhwcl9jb21iaW5lZCkpICMgTnVtYmVyIG9mIHNhbXBsZXMKCmNhdCgiTW9kdWxlLXRyYWl0IGNvcnJlbGF0aW9ucyBhbmQgcC12YWx1ZXMgY2FsY3VsYXRlZC5cblxuIikKCiMgUGxvdHRpbmcgdGhlIGhlYXRtYXAgdXNpbmcgbGFiZWxlZEhlYXRtYXAKIyBDdXN0b21pemUgdGV4dCBtYXRyaXggZm9yIGNvcnJlbGF0aW9uIGFuZCBwLXZhbHVlIGRpc3BsYXkKZGV2Lm9mZigpCnBuZygiZGlmZmNvZXhwL0RpZmZDb0V4cF9TeW50aGV0aWNfMC4xLnBuZyIsd2lkdGg9NixoZWlnaHQ9NSx1bml0cz0iaW4iLHJlcz02MDApCnRleHRNYXRyaXggPSBwYXN0ZShzaWduaWYobW9kdWxlVHJhaXRDb3IsIDIpLCAiXG4oIiwKICAgICAgICAgICAgICAgICAgICBzaWduaWYobW9kdWxlVHJhaXRQdmFsdWUsIDEpLCAiKSIsIHNlcCA9ICIiKTsKICAgICAgICAgICAgICAgICAgICBkaW0odGV4dE1hdHJpeCkgPSBkaW0obW9kdWxlVHJhaXRDb3IpCiAgICAgICAgICAgICAgICAgICAgcGFyKG1hciA9IGMoNiwgMTAsIDMsIDMpKQpwYXIobWFyID0gYyg4LCA4LCAyLCAxKSkgICMgUmVkdWNlIG1hcmdpbnMgKGJvdHRvbSwgbGVmdCwgdG9wLCByaWdodCkKbGFiZWxlZEhlYXRtYXAoTWF0cml4ID0gbW9kdWxlVHJhaXRDb3IsCiAgICAgICAgICAgICAgIHhMYWJlbHMgPSAiQ29uZGl0aW9uIiwKICAgICAgICAgICAgICAgeUxhYmVscyA9IG5hbWVzKE1Fc19kaWZmY29leCksCiAgICAgICAgICAgICAgIHlTeW1ib2xzID0gbmFtZXMoTUVzX2RpZmZjb2V4KSwKICAgICAgICAgICAgICAgY29sb3JMYWJlbHMgPSBGQUxTRSwKICAgICAgICAgICAgICAgY29sb3JzID0gYmx1ZVdoaXRlUmVkKDUwKSwgIyBSZWQgZm9yIHBvc2l0aXZlLCBibHVlIGZvciBuZWdhdGl2ZSBjb3JyZWxhdGlvbgogICAgICAgICAgICAgICB0ZXh0TWF0cml4ID0gdGV4dE1hdHJpeCwKICAgICAgICAgICAgICAgc2V0U3RkTWFyZ2lucyA9IEZBTFNFLAogICAgICAgICAgICAgICBjZXgudGV4dCA9IDAuNSwgIyBBZGp1c3QgdGV4dCBzaXplCiAgICAgICAgICAgICAgIHpsaW0gPSBjKC0xLCAxKSwKICAgICAgICAgICAgICAgbWFpbiA9IHBhc3RlKCJEaWZmQ29FeCBNb2R1bGVzIikKICAgICAgICAgICAgICAgKQoKY2F0KCJcbk1vZHVsZS1UcmFpdCBSZWxhdGlvbnNoaXAgSGVhdG1hcCBnZW5lcmF0ZWQuXG4iKQpkZXYub2ZmKCkKCmBgYAoKIyMgRS4yIEhpZ2ggdmFyaWFiaWxpdHkgKHA9MC45KQoKYGBge3J9CmxpYnJhcnkoV0dDTkEpCiMgV0dDTkEgb2Z0ZW4gcmVjb21tZW5kcyBzZXR0aW5nIHRoaXMKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmFsbG93V0dDTkFUaHJlYWRzKCkgIyBVc2UgbXVsdGlwbGUgY29yZXMgaWYgYXZhaWxhYmxlCgojIC0tLSBJbnB1dCBEYXRhIC0tLQojIFJlYWQgZXhwcmVzc2lvbiBkYXRhIGZvciB0d28gY29uZGl0aW9ucwojIEVuc3VyZSAncm93Lm5hbWVzID0gIkdlbmVzIicgY29ycmVjdGx5IGlkZW50aWZpZXMgdGhlIGdlbmUgSUQgY29sdW1uCmV4cHJzLjEgPSByZWFkLmNzdigiZGNvci1tZXRob2Qvc3ludGhldGljX2hlYWx0aHlfMC45LnR4dCIsIHNlcCA9ICJcdCIsIGhlYWRlcj1ULCByb3cubmFtZXMgPSAiR2VuZXMiKQpleHBycy4yID0gcmVhZC5jc3YoImRjb3ItbWV0aG9kL3N5bnRoZXRpY19kaXNlYXNlZF8wLjkudHh0Iiwgc2VwID0gIlx0IiwgaGVhZGVyPVQsIHJvdy5uYW1lcyA9ICJHZW5lcyIpCgojIEVuc3VyZSBnZW5lIG9yZGVyIGlzIGNvbnNpc3RlbnQgYWNyb3NzIGNvbmRpdGlvbnMKY29tbW9uR2VuZXMgPC0gaW50ZXJzZWN0KHJvd25hbWVzKGV4cHJzLjEpLCByb3duYW1lcyhleHBycy4yKSkKZXhwcnMuMSA8LSBleHBycy4xW2NvbW1vbkdlbmVzLCBdCmV4cHJzLjIgPC0gZXhwcnMuMltjb21tb25HZW5lcywgXQoKIyBJTVBPUlRBTlQgZm9yIFdHQ05BOiBUcmFuc3Bvc2UgZGF0YSBzbyBzYW1wbGVzIGFyZSByb3dzIGFuZCBnZW5lcyBhcmUgY29sdW1ucwpkYXRFeHByMSA8LSBhcy5kYXRhLmZyYW1lKHQoZXhwcnMuMSkpCmRhdEV4cHIyIDwtIGFzLmRhdGEuZnJhbWUodChleHBycy4yKSkKCiMgQ29tYmluZSBleHByZXNzaW9uIGRhdGEgZm9yIG1vZHVsZSBlaWdlbmdlbmUgY2FsY3VsYXRpb24gYW5kIHRyYWl0IG1hcHBpbmcKIyBUaGUgY29sdW1uIG5hbWVzIChnZW5lcykgbXVzdCBiZSBpZGVudGljYWwgZm9yIGNiaW5kIHRvIHdvcmsgY29ycmVjdGx5CmRhdEV4cHJfY29tYmluZWQgPC0gdChjYmluZChleHBycy4xLCBleHBycy4yKSkKCmNhdCgiRGF0YSBkaW1lbnNpb25zIGZvciBXR0NOQSAoc2FtcGxlcyB4IGdlbmVzKTpcbiIpCmNhdCgiQ29uZGl0aW9uIDE6IiwgZGltKGRhdEV4cHIxKSwgIlxuIikKY2F0KCJDb25kaXRpb24gMjoiLCBkaW0oZGF0RXhwcjIpLCAiXG4iKQpjYXQoIkNvbWJpbmVkIGRhdGE6IiwgZGltKGRhdEV4cHJfY29tYmluZWQpLCAiXG5cbiIpCgojIC0tLSBEZWZpbmUgU29mdC10aHJlc2hvbGRpbmcgUG93ZXIgKGJldGEpIC0tLQojIEFzIHBlciB0aGUgcGFwZXIsIGJldGEgaXMgYSB0dW5pbmcgcGFyYW1ldGVyIGZvciB0aGUgYWRqYWNlbmN5IGRpZmZlcmVuY2UgbWF0cml4LgojIEl0cyBjaG9pY2UgaW1wYWN0cyB0aGUgc3RyaW5nZW5jeSBvZiBkaWZmZXJlbnRpYWwgY29ycmVsYXRpb24uCmJldGEgPSAxICMgRXhhbXBsZSB2YWx1ZSwgYWRqdXN0IGFzIG5lZWRlZCBiYXNlZCBvbiB5b3VyIGRhdGEgYW5kIGRlc2lyZWQgc3RyaW5nZW5jeQoKCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CiMgRGlmZkNvRXggQWxnb3JpdGhtIFN0ZXBzCiMgPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09CgojIC0tLSBTdGVwIDE6IEJ1aWxkIGNvcnJlbGF0aW9uIG1hdHJpeCBDW2tdIHdpdGhpbiBlYWNoIGNvbmRpdGlvbiBrIC0tLQpjYXQoIlN0ZXAgMTogQ29tcHV0aW5nIGNvcnJlbGF0aW9uIG1hdHJpY2VzIGZvciBlYWNoIGNvbmRpdGlvbi4uLlxuIikKY29yTWF0cml4MSA8LSBXR0NOQTo6Y29yKGRhdEV4cHIxLCBtZXRob2QgPSAic3BlYXJtYW4iLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKY29yTWF0cml4MiA8LSBXR0NOQTo6Y29yKGRhdEV4cHIyLCBtZXRob2QgPSAic3BlYXJtYW4iLCB1c2UgPSAicGFpcndpc2UuY29tcGxldGUub2JzIikKCmNhdCgiQ29ycmVsYXRpb24gbWF0cmljZXMgY29tcHV0ZWQuXG5cbiIpCgojIC0tLSBTdGVwIDI6IENvbXB1dGUgbWF0cml4IG9mIGFkamFjZW5jeSBkaWZmZXJlbmNlIEQgLS0tCiMgZF9paiA9IHxzaWduKGNvcl9pal4oMSkpKGNvcl9pal4oMSkpXjIgLSBzaWduKGNvcl9pal4oMikpKGNvcl9pal4oMikpXjJ8XmJldGEKY2F0KCJTdGVwIDI6IENvbXB1dGluZyB0aGUgYWRqYWNlbmN5IGRpZmZlcmVuY2UgbWF0cml4IEQuLi5cbiIpCgojIEdldCBhbGwgdW5pcXVlIGdlbmVzIChzaG91bGQgYmUgY29tbW9uR2VuZXMgZnJvbSBhYm92ZSkKYWxsR2VuZXMgPC0gY29sbmFtZXMoZGF0RXhwcjEpCgojIEluaXRpYWxpemUgRCBtYXRyaXggd2l0aCB6ZXJvcwpEX21hdHJpeCA8LSBtYXRyaXgoMCwKICAgICAgICAgICAgICAgICAgIG5yb3cgPSBsZW5ndGgoYWxsR2VuZXMpLAogICAgICAgICAgICAgICAgICAgbmNvbCA9IGxlbmd0aChhbGxHZW5lcyksCiAgICAgICAgICAgICAgICAgICBkaW1uYW1lcyA9IGxpc3QoYWxsR2VuZXMsIGFsbEdlbmVzKSkKCiMgTG9vcCB0aHJvdWdoIGFsbCBnZW5lIHBhaXJzIHRvIGNhbGN1bGF0ZSBEX2lqCmZvciAoaSBpbiAxOmxlbmd0aChhbGxHZW5lcykpIHsKICBmb3IgKGogaW4gaTpsZW5ndGgoYWxsR2VuZXMpKSB7ICMgTG9vcCBqIGZyb20gaSBmb3IgdXBwZXIgdHJpYW5nbGUgKGFuZCBzeW1tZXRyeSkKICAgIGdlbmVfaSA8LSBhbGxHZW5lc1tpXQogICAgZ2VuZV9qIDwtIGFsbEdlbmVzW2pdCgogICAgIyBHZXQgY29ycmVsYXRpb25zIGZvciB0aGUgcGFpcgogICAgY29yX2lqXzEgPC0gY29yTWF0cml4MVtnZW5lX2ksIGdlbmVfal0KICAgIGNvcl9pal8yIDwtIGNvck1hdHJpeDJbZ2VuZV9pLCBnZW5lX2pdCgogICAgIyBIYW5kbGUgcG90ZW50aWFsIE5BIGNvcnJlbGF0aW9ucwogICAgIyBJZiBhIGNvcnJlbGF0aW9uIGlzIE5BIGluIGVpdGhlciBjb25kaXRpb24sIHNldCBkaWZmZXJlbnRpYWwgdG8gMCBmb3IgdGhhdCBwYWlyCiAgICBpZiAoaXMubmEoY29yX2lqXzEpIHx8IGlzLm5hKGNvcl9pal8yKSkgewogICAgICBEX21hdHJpeFtnZW5lX2ksIGdlbmVfal0gPC0gMAogICAgICBEX21hdHJpeFtnZW5lX2osIGdlbmVfaV0gPC0gMAogICAgICBuZXh0CiAgICB9CgogICAgIyBDYWxjdWxhdGUgc2lnbmVkIHNxdWFyZWQgY29ycmVsYXRpb25zCiAgICBzX2lqXzEgPC0gc2lnbihjb3JfaWpfMSkgKiAoY29yX2lqXzFeMikKICAgIHNfaWpfMiA8LSBzaWduKGNvcl9pal8yKSAqIChjb3JfaWpfMl4yKQoKICAgICMgQ2FsY3VsYXRlIGRfaWogYXMgcGVyIHBhcGVyJ3MgZm9ybXVsYTogfHNfaWpfMSAtIHNfaWpfMnxeYmV0YQogICAgZF9pal92YWwgPC0gYWJzKHNfaWpfMSAtIHNfaWpfMileYmV0YQoKICAgICMgUG9wdWxhdGUgRF9tYXRyaXggc3ltbWV0cmljYWxseQogICAgRF9tYXRyaXhbZ2VuZV9pLCBnZW5lX2pdIDwtIGRfaWpfdmFsCiAgICBEX21hdHJpeFtnZW5lX2osIGdlbmVfaV0gPC0gZF9pal92YWwgIyBFbnN1cmUgc3ltbWV0cnkKICB9Cn0KCmNhdCgiQWRqYWNlbmN5IGRpZmZlcmVuY2UgbWF0cml4IEQgY29tcHV0ZWQuIEl0cyByYW5nZSBpcyBbIiwgbWluKERfbWF0cml4KSwgIiwgIiwgbWF4KERfbWF0cml4KSwgIl1cblxuIikKCiMgLS0tIFN0ZXAgMzogRGVyaXZlIHRoZSBUb3BvbG9naWNhbCBPdmVybGFwIChUTykgYmFzZWQgZGlzc2ltaWxhcml0eSBtYXRyaXggVCBmcm9tIEQgLS0tCmNhdCgiU3RlcCAzOiBDb21wdXRpbmcgVG9wb2xvZ2ljYWwgT3ZlcmxhcCBEaXNzaW1pbGFyaXR5IChUT00pIGZyb20gRC4uLlxuIikKCiMgSXQncyBnb29kIHByYWN0aWNlIHRvIGVuc3VyZSB0aGUgZGlhZ29uYWwgaXMgMSBmb3IgYWRqYWNlbmN5IG1hdHJpY2VzIGJlZm9yZSBUT00KZGlhZyhEX21hdHJpeCkgPC0gMSAjIEdlbmUgaXMgcGVyZmVjdGx5ICJkaWZmZXJlbnRpYWxseSBjb2V4cHJlc3NlZCIgd2l0aCBpdHNlbGYgY29uY2VwdHVhbGx5CgojIFVzZSBXR0NOQSdzIFRPTXNpbWlsYXJpdHkgZnVuY3Rpb24gb24gb3VyIGN1c3RvbSBkaWZmZXJlbnRpYWwgYWRqYWNlbmN5IG1hdHJpeCBELgojIFRPTVR5cGU9InVuc2lnbmVkIiBpcyBnZW5lcmFsbHkgYXBwcm9wcmlhdGUgZm9yIERfbWF0cml4IHZhbHVlcyB3aGljaCBhcmUgWzAsMV0uClRPTV9tYXRyaXggPC0gVE9Nc2ltaWxhcml0eShEX21hdHJpeCwgVE9NVHlwZSA9ICJ1bnNpZ25lZCIpCgojIENvbnZlcnQgVE9NIHRvIGRpc3NpbWlsYXJpdHkgKDEgLSBUT00pClRfZGlzc2ltaWxhcml0eSA8LSAxIC0gVE9NX21hdHJpeAoKY2F0KCJUb3BvbG9naWNhbCBPdmVybGFwIERpc3NpbWlsYXJpdHkgbWF0cml4IFQgY29tcHV0ZWQuXG5cbiIpCgojIC0tLSBTdGVwIDQ6IENsdXN0ZXJpbmcgYW5kIE1vZHVsZSBJZGVudGlmaWNhdGlvbiAtLS0KY2F0KCJTdGVwIDQ6IFBlcmZvcm1pbmcgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgYW5kIG1vZHVsZSBpZGVudGlmaWNhdGlvbi4uLlxuIikKCiMgSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcKZ2VuZVRyZWVfZGlmZmNvZXggPC0gaGNsdXN0KGFzLmRpc3QoVF9kaXNzaW1pbGFyaXR5KSwgbWV0aG9kID0gImF2ZXJhZ2UiKQoKIyBQbG90IHRoZSBkZW5kcm9ncmFtCnBsb3QoZ2VuZVRyZWVfZGlmZmNvZXgsIHhsYWIgPSAiIiwgc3ViID0gIiIsIG1haW4gPSAiR2VuZSBkZW5kcm9ncmFtIGJhc2VkIG9uIGRpZmZlcmVudGlhbCBUT00iLAogICAgIGxhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wNCkKCiMgTW9kdWxlIGlkZW50aWZpY2F0aW9uIHVzaW5nIGR5bmFtaWNUcmVlQ3V0Cm1pbk1vZHVsZVNpemVfZGlmZmNvZXggPC0gMTAgIyBBZGp1c3QgYmFzZWQgb24geW91ciBkYXRhIGFuZCBkZXNpcmVkIG1vZHVsZSBzaXplCmR5bmFtaWNNb2RzX2RpZmZjb2V4IDwtIGN1dHJlZUR5bmFtaWMoZGVuZHJvID0gZ2VuZVRyZWVfZGlmZmNvZXgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzdE0gPSBUX2Rpc3NpbWlsYXJpdHksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVlcFNwbGl0ID0gMiwgIyBBZGp1c3QgZGVlcFNwbGl0IGZvciBncmFudWxhcml0eSAoMC00KQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbVJlc3BlY3RzRGVuZHJvID0gRkFMU0UsICMgU2V0IHRvIFRSVUUgZm9yIFBBTSByZWZpbmVtZW50CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluQ2x1c3RlclNpemUgPSBtaW5Nb2R1bGVTaXplX2RpZmZjb2V4KQoKIyBBc3NpZ24gbW9kdWxlIGNvbG9ycyAoZm9yIHZpc3VhbGl6YXRpb24gYW5kIGZ1cnRoZXIgYW5hbHlzaXMpCm1vZHVsZUNvbG9yc19kaWZmY29leCA8LSBsYWJlbHMyY29sb3JzKGR5bmFtaWNNb2RzX2RpZmZjb2V4KQoKY2F0KCJNb2R1bGUgaWRlbnRpZmljYXRpb24gY29tcGxldGUuXG4iKQpjYXQoIk51bWJlciBvZiBkaWZmZXJlbnRpYWwgY28tZXhwcmVzc2lvbiBtb2R1bGVzIGZvdW5kOiIsIGxlbmd0aCh1bmlxdWUobW9kdWxlQ29sb3JzX2RpZmZjb2V4KSkgLSAxLAogICAgIiAoZXhjbHVkaW5nIGdyZXkgZm9yIHVuYXNzaWduZWQgZ2VuZXMpXG4iKSAjIC0xIGZvciAnZ3JleScgbW9kdWxlCmNhdCgiU2l6ZXMgb2YgbW9kdWxlczpcbiIpCnByaW50KHRhYmxlKG1vZHVsZUNvbG9yc19kaWZmY29leCkpCgojIE91dHB1dCB0aGUgbW9kdWxlIGFzc2lnbm1lbnRzIHRvIGEgZGF0YSBmcmFtZQpkaWZmZXJlbnRpYWxfbW9kdWxlcyA8LSBkYXRhLmZyYW1lKAogIEdlbmUgPSBjb2xuYW1lcyhkYXRFeHByMSksCiAgTW9kdWxlID0gbW9kdWxlQ29sb3JzX2RpZmZjb2V4CikKCiMgU2F2ZSB0aGUgZGF0YWZyYW1lIHRvIGEgQ1NWIGZpbGUKb3V0cHV0X2ZpbGUgPC0gImRpZmZjb2V4cC9tb2R1bGVDb2xvcnNfc3ludGhldGljXzAuOS5jc3YiCndyaXRlLmNzdihkaWZmZXJlbnRpYWxfbW9kdWxlcywgZmlsZSA9IG91dHB1dF9maWxlLCByb3cubmFtZXMgPSBGKQpjYXQocGFzdGUwKCJcbk1vZHVsZSBhc3NpZ25tZW50cyBzYXZlZCB0byAiLCBvdXRwdXRfZmlsZSwgIlxuIikpCgojIC0tLSBOZXcgU2VjdGlvbjogUGxvdHRpbmcgTW9kdWxlLVRyYWl0IFJlbGF0aW9uc2hpcHMgLS0tCgpjYXQoIlxuLS0tIFBsb3R0aW5nIE1vZHVsZS1UcmFpdCBSZWxhdGlvbnNoaXBzIC0tLVxuIikKCiMgQ3JlYXRlIGEgdHJhaXQgZGF0YSBmcmFtZSBmb3IgeW91ciBzYW1wbGVzCiMgVGhlIHJvdyBuYW1lcyBvZiB0aGUgdHJhaXQgZGF0YSBmcmFtZSBtdXN0IG1hdGNoIHRoZSBzYW1wbGUgbmFtZXMgaW4gZGF0RXhwcl9jb21iaW5lZAp0cmFpdF9kYXRhIDwtIGRhdGEuZnJhbWUoCiAgU2FtcGxlID0gYyhyb3duYW1lcyhkYXRFeHByMSksIHJvd25hbWVzKGRhdEV4cHIyKSksCiAgQ29uZGl0aW9uID0gYyhyZXAoIkhlYWx0aHkiLCBucm93KGRhdEV4cHIxKSksIHJlcCgiRGlzZWFzZWQiLCBucm93KGRhdEV4cHIyKSkpCikKcm93bmFtZXModHJhaXRfZGF0YSkgPC0gdHJhaXRfZGF0YSRTYW1wbGUgIyBTZXQgc2FtcGxlIG5hbWVzIGFzIHJvdyBuYW1lcwoKIyBDb252ZXJ0IGNhdGVnb3JpY2FsIHRyYWl0IHRvIG51bWVyaWMgZm9yIGNvcnJlbGF0aW9uIChXR0NOQSBleHBlY3RzIG51bWVyaWMpCiMgMCBmb3IgSGVhbHRoeSwgMSBmb3IgRGlzZWFzZWQgKGFyYml0cmFyeSBhc3NpZ25tZW50KQp0cmFpdF9udW1lcmljIDwtIGFzLmRhdGEuZnJhbWUoYXMubnVtZXJpYyh0cmFpdF9kYXRhJENvbmRpdGlvbiA9PSAiRGlzZWFzZWQiKSkKcm93bmFtZXModHJhaXRfbnVtZXJpYykgPC0gcm93bmFtZXModHJhaXRfZGF0YSkKY29sbmFtZXModHJhaXRfbnVtZXJpYykgPC0gIkRpc2Vhc2VkX3ZzX0hlYWx0aHkiCgpjYXQoIlRyYWl0IGRhdGEgcHJlcGFyZWQuXG4iKQpwcmludChoZWFkKHRyYWl0X251bWVyaWMpKQpjYXQoIlxuIikKCiMgQ2FsY3VsYXRlIE1vZHVsZSBFaWdlbmdlbmVzIChNRXMpCiMgTUVzIHJlcHJlc2VudCB0aGUgImF2ZXJhZ2UiIGV4cHJlc3Npb24gcHJvZmlsZSBvZiBlYWNoIG1vZHVsZS4KIyBVc2UgdGhlIGNvbWJpbmVkIGV4cHJlc3Npb24gZGF0YSBhbmQgdGhlIGlkZW50aWZpZWQgbW9kdWxlIGNvbG9ycy4KTUVzX2RpZmZjb2V4IDwtIG1vZHVsZUVpZ2VuZ2VuZXMoZGF0RXhwcl9jb21iaW5lZCwgbW9kdWxlQ29sb3JzX2RpZmZjb2V4KSRlaWdlbmdlbmVzCmNhdCgiTW9kdWxlIEVpZ2VuZ2VuZXMgY2FsY3VsYXRlZC5cbiIpCnByaW50KGhlYWQoTUVzX2RpZmZjb2V4KSkKY2F0KCJcbiIpCgojIFJlbGF0ZSBNRXMgdG8gdGhlIHRyYWl0IChDb25kaXRpb24pCiMgQ2FsY3VsYXRlIGNvcnJlbGF0aW9ucyBhbmQgdGhlaXIgcC12YWx1ZXMKbW9kdWxlVHJhaXRDb3IgPC0gV0dDTkE6OmNvcihNRXNfZGlmZmNvZXgsIHRyYWl0X251bWVyaWMsIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAicGVhcnNvbiIpICMgUGVhcnNvbiBpcyBjb21tb24gZm9yIE1Fcwptb2R1bGVUcmFpdFB2YWx1ZSA8LSBXR0NOQTo6Y29yUHZhbHVlU3R1ZGVudChtb2R1bGVUcmFpdENvciwgbnJvdyhkYXRFeHByX2NvbWJpbmVkKSkgIyBOdW1iZXIgb2Ygc2FtcGxlcwoKY2F0KCJNb2R1bGUtdHJhaXQgY29ycmVsYXRpb25zIGFuZCBwLXZhbHVlcyBjYWxjdWxhdGVkLlxuXG4iKQoKIyBQbG90dGluZyB0aGUgaGVhdG1hcCB1c2luZyBsYWJlbGVkSGVhdG1hcAojIEN1c3RvbWl6ZSB0ZXh0IG1hdHJpeCBmb3IgY29ycmVsYXRpb24gYW5kIHAtdmFsdWUgZGlzcGxheQpkZXYub2ZmKCkKcG5nKCJkaWZmY29leHAvRGlmZkNvRXhwX1N5bnRoZXRpY18wLjkucG5nIix3aWR0aD02LGhlaWdodD01LHVuaXRzPSJpbiIscmVzPTYwMCkKdGV4dE1hdHJpeCA9IHBhc3RlKHNpZ25pZihtb2R1bGVUcmFpdENvciwgMiksICJcbigiLAogICAgICAgICAgICAgICAgICAgIHNpZ25pZihtb2R1bGVUcmFpdFB2YWx1ZSwgMSksICIpIiwgc2VwID0gIiIpOwogICAgICAgICAgICAgICAgICAgIGRpbSh0ZXh0TWF0cml4KSA9IGRpbShtb2R1bGVUcmFpdENvcikKICAgICAgICAgICAgICAgICAgICBwYXIobWFyID0gYyg2LCAxMCwgMywgMykpCnBhcihtYXIgPSBjKDgsIDgsIDIsIDEpKSAgIyBSZWR1Y2UgbWFyZ2lucyAoYm90dG9tLCBsZWZ0LCB0b3AsIHJpZ2h0KQpsYWJlbGVkSGVhdG1hcChNYXRyaXggPSBtb2R1bGVUcmFpdENvciwKICAgICAgICAgICAgICAgeExhYmVscyA9ICJDb25kaXRpb24iLAogICAgICAgICAgICAgICB5TGFiZWxzID0gbmFtZXMoTUVzX2RpZmZjb2V4KSwKICAgICAgICAgICAgICAgeVN5bWJvbHMgPSBuYW1lcyhNRXNfZGlmZmNvZXgpLAogICAgICAgICAgICAgICBjb2xvckxhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICBjb2xvcnMgPSBibHVlV2hpdGVSZWQoNTApLCAjIFJlZCBmb3IgcG9zaXRpdmUsIGJsdWUgZm9yIG5lZ2F0aXZlIGNvcnJlbGF0aW9uCiAgICAgICAgICAgICAgIHRleHRNYXRyaXggPSB0ZXh0TWF0cml4LAogICAgICAgICAgICAgICBzZXRTdGRNYXJnaW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgIGNleC50ZXh0ID0gMC41LCAjIEFkanVzdCB0ZXh0IHNpemUKICAgICAgICAgICAgICAgemxpbSA9IGMoLTEsIDEpLAogICAgICAgICAgICAgICBtYWluID0gcGFzdGUoIkRpZmZDb0V4IE1vZHVsZXMiKQogICAgICAgICAgICAgICApCgpjYXQoIlxuTW9kdWxlLVRyYWl0IFJlbGF0aW9uc2hpcCBIZWF0bWFwIGdlbmVyYXRlZC5cbiIpCmRldi5vZmYoKQpgYGAKCmBgYHtyfQpleHBycy5oID0gcmVhZC5jc3YoImRjb3ItbWV0aG9kL3N5bnRoZXRpY19oZWFsdGh5XzAuOS50eHQiLCBzZXAgPSAiXHQiLCBoZWFkZXI9VCwgcm93Lm5hbWVzID0gIkdlbmVzIikKZXhwcnMuZCA9IHJlYWQuY3N2KCJkY29yLW1ldGhvZC9zeW50aGV0aWNfZGlzZWFzZWRfMC45LnR4dCIsIHNlcCA9ICJcdCIsIGhlYWRlcj1ULCByb3cubmFtZXMgPSAiR2VuZXMiKQphbGxvd1dHQ05BVGhyZWFkcygpCgojIERpc2Vhc2VkIC0gSGVhbHRoeTsgaGlnaCBjb3JyZWxhdGlvbiAtLT4gZGlzZWFzZWQKcmVzLmhpZ2ggPWRpZmZjb2V4cChleHBycy4xID0gZXhwcnMuZCwgZXhwcnMuMiA9IGV4cHJzLmgsCiAgICAgICAgICAgICAgICAgICByLm1ldGhvZCA9ICJwZWFyc29uIixxLm1ldGhvZCA9ICJib25mZXJyb25pIikKYGBgCgojIFguIFByZWNpc2lvbiAmIFJlY2FsbAoKYGBge3J9CmV2YWx1YXRlX21ldGhvZCA8LSBmdW5jdGlvbih0cnVlX3Bvc2l0aXZlX2dlbmVzLCBwcmVkaWN0ZWRfcG9zaXRpdmVfbW9kdWxlcywgZ2VuZV9tb2R1bGVfZGYpIHsKICBnZW5lX21vZHVsZV9kZiA8LSBnZW5lX21vZHVsZV9kZlshaXMubmEoZ2VuZV9tb2R1bGVfZGYkZ2VuZSkgJiAhaXMubmEoZ2VuZV9tb2R1bGVfZGYkbW9kdWxlKSwgXQogIGFsbF9nZW5lcyA8LSB1bmlxdWUoZ2VuZV9tb2R1bGVfZGYkZ2VuZSkKCiAgIyBIYW5kbGUgZW1wdHkgcHJlZGljdGVkIG1vZHVsZXMKICBpZiAobGVuZ3RoKHByZWRpY3RlZF9wb3NpdGl2ZV9tb2R1bGVzKSA9PSAwKSB7CiAgICBwcmVkaWN0ZWRfcG9zaXRpdmVfZ2VuZXMgPC0gY2hhcmFjdGVyKDApCiAgfSBlbHNlIHsKICAgIHByZWRpY3RlZF9wb3NpdGl2ZV9nZW5lcyA8LSBnZW5lX21vZHVsZV9kZiRnZW5lW2dlbmVfbW9kdWxlX2RmJG1vZHVsZSAlaW4lIHByZWRpY3RlZF9wb3NpdGl2ZV9tb2R1bGVzXQogIH0KCiAgVFAgPC0gc3VtKHByZWRpY3RlZF9wb3NpdGl2ZV9nZW5lcyAlaW4lIHRydWVfcG9zaXRpdmVfZ2VuZXMpCiAgRlAgPC0gc3VtKCEocHJlZGljdGVkX3Bvc2l0aXZlX2dlbmVzICVpbiUgdHJ1ZV9wb3NpdGl2ZV9nZW5lcykpCiAgRk4gPC0gc3VtKHRydWVfcG9zaXRpdmVfZ2VuZXMgJWluJSBhbGxfZ2VuZXMgJiAhKHRydWVfcG9zaXRpdmVfZ2VuZXMgJWluJSBwcmVkaWN0ZWRfcG9zaXRpdmVfZ2VuZXMpKQogIFROIDwtIHN1bSghKGFsbF9nZW5lcyAlaW4lIHRydWVfcG9zaXRpdmVfZ2VuZXMpICYgIShhbGxfZ2VuZXMgJWluJSBwcmVkaWN0ZWRfcG9zaXRpdmVfZ2VuZXMpKQoKICBwcmVjaXNpb24gPC0gaWZlbHNlKChUUCArIEZQKSA+IDAsIFRQIC8gKFRQICsgRlApLCBOQSkKICByZWNhbGwgPC0gaWZlbHNlKChUUCArIEZOKSA+IDAsIFRQIC8gKFRQICsgRk4pLCBOQSkKCiAgcmV0dXJuKGRhdGEuZnJhbWUoCiAgICBUUCA9IFRQLCBGUCA9IEZQLCBUTiA9IFROLCBGTiA9IEZOLAogICAgcHJlY2lzaW9uID0gcHJlY2lzaW9uLAogICAgcmVjYWxsID0gcmVjYWxsCiAgKSkKfQpgYGAKCiMjIFguMSBMb3cgdmFyaWFiaWxpdHkgKHA9MC4xKQoKYGBge3J9CiMgSW5wdXRzCnN5bnRoZXRpY19kYXRhID0gcmVhZC5jc3YoInN5bnRoZXRpY19leHByZXNzaW9uX2RhdGFfZWZmZWN0XzAuMS5jc3YiLCBoZWFkZXI9VCkKdHJ1ZV9wb3NfZGlzZWFzZWRfZ2VuZXMgPC0gc3ludGhldGljX2RhdGEkWFsxNTE6MzAwXQoKIyBNb2R1bGUgbWFwcGluZwpnZW5lX21vZHVsZV9maWxlcyA8LSBsaXN0KAogIFdHQ05BID0gIndnY25hL21vZHVsZUNvbG9yc19XR0NOQV8wLjEuY3N2IiwKICBEaWZmQ29FeHAgPSAiZGlmZmNvZXhwL21vZHVsZUNvbG9yc19zeW50aGV0aWNfMC4xLmNzdiIsCiAgREdDQSA9ICJ3Z2NuYS9tb2R1bGVDb2xvcnNfV0dDTkFfMC4xLmNzdiIsCiAgT3VyX01ldGhvZCA9IGxpc3QoImRjb3ItbWV0aG9kL21vZHVsZXNfZGlzZWFzZWRfMC4xLmNzdiIsCiAgICAgICAgICAgICAgICAgICAgImRjb3ItbWV0aG9kL21vZHVsZXNfaGVhbHRoeV8wLjEuY3N2IikKICApCgojIFBvc2l0aXZlbHkgY29ycmVsYXRlZCBtb2R1bGVzCnByZWRpY3RlZF9tb2R1bGVzX2J5X21ldGhvZCA9IGxpc3QoCiAgV0dDTkEgPSBjaGFyYWN0ZXIoMCksCiAgRGlmZkNvRXhwID0gY2hhcmFjdGVyKDApLAogIERHQ0E9IGMoImJyb3duIiksCiAgT3VyX01ldGhvZCA9ICBsaXN0KGMoInR1cnF1b2lzZSIsICJyZWQiLCAibWFnZW50YSIsICJicm93biIsICJ5ZWxsb3ciLCAicHVycGxlIiksCiAgICAgICAgICAgICAgICAgICAgIGMoInllbGxvdyIsICJibHVlIiwgInJlZCIsICJicm93biIpCiAgKQopCgojIEV2YWx1YXRpb24gbG9vcApyZXN1bHRzX2xpc3QgPC0gbGlzdCgpCgpmb3IgKG1ldGhvZCBpbiBuYW1lcyhnZW5lX21vZHVsZV9maWxlcykpIHsKICBmaWxlcyA8LSBnZW5lX21vZHVsZV9maWxlc1tbbWV0aG9kXV0KICBtb2R1bGVzIDwtIHByZWRpY3RlZF9tb2R1bGVzX2J5X21ldGhvZFtbbWV0aG9kXV0KICAKICAjIENvbWJpbmUgYWxsIGRhdGFmcmFtZXMgZm9yIHRoaXMgbWV0aG9kCiAgY29tYmluZWRfZGYgPC0gZGF0YS5mcmFtZSgpCiAgCiAgZm9yIChpIGluIHNlcV9hbG9uZyhmaWxlcykpIHsKICAgIGRmIDwtIHJlYWQuY3N2KGZpbGVzW1tpXV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICAgIGNvbG5hbWVzKGRmKSA8LSBjKCJnZW5lIiwgIm1vZHVsZSIpCgogICAgIyBBcHBlbmQgd2l0aCBmaWxlIElEIGlmIG5lZWRlZCAob3B0aW9uYWwpCiAgICBjb21iaW5lZF9kZiA8LSByYmluZChjb21iaW5lZF9kZiwgZGYpCiAgfQoKICAjIFJlbW92ZSBkdXBsaWNhdGVzIChpbiBjYXNlIHNhbWUgZ2VuZSBhcHBlYXJzIG11bHRpcGxlIHRpbWVzKQogIGNvbWJpbmVkX2RmIDwtIHVuaXF1ZShjb21iaW5lZF9kZikKCiAgIyBEZXRlcm1pbmUgY29tYmluZWQgcHJlZGljdGVkIG1vZHVsZXMKICBpZiAoaXMuY2hhcmFjdGVyKG1vZHVsZXMpKSB7CiAgICBwcmVkaWN0ZWRfbW9kdWxlcyA8LSBtb2R1bGVzCiAgfSBlbHNlIGlmIChpcy5saXN0KG1vZHVsZXMpKSB7CiAgICBwcmVkaWN0ZWRfbW9kdWxlcyA8LSB1bmlxdWUodW5saXN0KG1vZHVsZXMpKQogIH0gZWxzZSB7CiAgICBzdG9wKHBhc3RlKCJJbnZhbGlkIHByZWRpY3RlZCBtb2R1bGVzIGZvcm1hdCBmb3IgbWV0aG9kIiwgbWV0aG9kKSkKICB9CgogICMgRXZhbHVhdGUgb25jZSBmb3IgdGhlIGNvbWJpbmVkIGdlbmUtbW9kdWxlIG1hcHBpbmcKICByZXMgPC0gZXZhbHVhdGVfbWV0aG9kKAogICAgdHJ1ZV9wb3NpdGl2ZV9nZW5lcyA9IHRydWVfcG9zX2Rpc2Vhc2VkX2dlbmVzLAogICAgcHJlZGljdGVkX3Bvc2l0aXZlX21vZHVsZXMgPSBwcmVkaWN0ZWRfbW9kdWxlcywKICAgIGdlbmVfbW9kdWxlX2RmID0gY29tYmluZWRfZGYKICApCgogIHJlc3VsdHNfbGlzdFtbbWV0aG9kXV0gPC0gZGF0YS5mcmFtZSgKICAgIG1ldGhvZCA9IG1ldGhvZCwKICAgIFRQID0gcmVzJFRQLAogICAgRlAgPSByZXMkRlAsCiAgICBUTiA9IHJlcyRUTiwKICAgIEZOID0gcmVzJEZOLAogICAgcHJlY2lzaW9uID0gcmVzJHByZWNpc2lvbioxMDAsCiAgICByZWNhbGwgPSByZXMkcmVjYWxsKjEwMCwKICAgIHJvdy5uYW1lcyA9IE5VTEwKICApCn0KCiMgQ29tYmluZSBhbGwgcmVzdWx0cyBpbnRvIG9uZSBzdW1tYXJ5IGRhdGEgZnJhbWUKc3VtbWFyeV9kZiA8LSBkby5jYWxsKHJiaW5kLCByZXN1bHRzX2xpc3QpCnJvd25hbWVzKHN1bW1hcnlfZGYpIDwtIHN1bW1hcnlfZGYkbWV0aG9kCnN1bW1hcnlfZGYkbWV0aG9kIDwtIE5VTEwKCiMgVmlldyByZXN1bHRzCnByaW50KHN1bW1hcnlfZGYpCmBgYAoKIyMgWC4yIEhpZ2ggdmFyaWFiaWxpdHkgKHA9MC45KQoKYGBge3J9CiMgSW5wdXRzCnN5bnRoZXRpY19kYXRhID0gcmVhZC5jc3YoInN5bnRoZXRpY19leHByZXNzaW9uX2RhdGFfZWZmZWN0XzAuOS5jc3YiLCBoZWFkZXI9VCkKdHJ1ZV9wb3NfZGlzZWFzZWRfZ2VuZXMgPC0gc3ludGhldGljX2RhdGEkWFsxNTE6MzAwXQoKIyBNb2R1bGUgbWFwcGluZwpnZW5lX21vZHVsZV9maWxlcyA8LSBsaXN0KAogIFdHQ05BID0gIndnY25hL21vZHVsZUNvbG9yc19XR0NOQV8wLjkuY3N2IiwKICBEaWZmQ29FeHAgPSAiZGlmZmNvZXhwL21vZHVsZUNvbG9yc19zeW50aGV0aWNfMC45LmNzdiIsCiAgREdDQSA9ICJ3Z2NuYS9tb2R1bGVDb2xvcnNfV0dDTkFfMC45LmNzdiIsCiAgT3VyX01ldGhvZCA9IGxpc3QoImRjb3ItbWV0aG9kL21vZHVsZXNfZGlzZWFzZWRfMC45LmNzdiIsCiAgICAgICAgICAgICAgICAgICAgImRjb3ItbWV0aG9kL21vZHVsZXNfaGVhbHRoeV8wLjkuY3N2IikKICApCgojIFBvc2l0aXZlbHkgY29ycmVsYXRlZCBtb2R1bGVzCnByZWRpY3RlZF9tb2R1bGVzX2J5X21ldGhvZCA9IGxpc3QoCiAgV0dDTkEgPSBjKCJibHVlIiwgImJsYWNrIiwgInJlZCIsICJtYWdlbnRhIiwgImdyZWVueWVsbG93IiwgInB1cnBsZSIpLAogIERpZmZDb0V4cCA9IGMoImJsdWUiLCAiZ3JlZW4iLCAiZ3JlZW55ZWxsb3ciLCAicGluayIsICJwdXJwbGUiLCAieWVsbG93IiksCiAgREdDQSA9IGMoInR1cnF1b2lzZSIsImdyZWVueWVsbG93IiwibWFnZW50YSIsImJsYWNrIiwicGluayIpLAogIE91cl9NZXRob2QgPSAgbGlzdChjKCJibHVlIiwgImdyZWVuIiwgInBpbmsiLCAiYnJvd24iKSwKICAgICAgICAgICAgICAgICAgICAgYygicGluayIsICJibHVlIiwgInllbGxvdyIsICJibGFjayIpCiAgKQopCgojIEV2YWx1YXRpb24gbG9vcApyZXN1bHRzX2xpc3QgPC0gbGlzdCgpCgpmb3IgKG1ldGhvZCBpbiBuYW1lcyhnZW5lX21vZHVsZV9maWxlcykpIHsKICBmaWxlcyA8LSBnZW5lX21vZHVsZV9maWxlc1tbbWV0aG9kXV0KICBtb2R1bGVzIDwtIHByZWRpY3RlZF9tb2R1bGVzX2J5X21ldGhvZFtbbWV0aG9kXV0KICAKICAjIENvbWJpbmUgYWxsIGRhdGFmcmFtZXMgZm9yIHRoaXMgbWV0aG9kCiAgY29tYmluZWRfZGYgPC0gZGF0YS5mcmFtZSgpCiAgCiAgZm9yIChpIGluIHNlcV9hbG9uZyhmaWxlcykpIHsKICAgIGRmIDwtIHJlYWQuY3N2KGZpbGVzW1tpXV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKICAgIGNvbG5hbWVzKGRmKSA8LSBjKCJnZW5lIiwgIm1vZHVsZSIpCgogICAgIyBBcHBlbmQgd2l0aCBmaWxlIElEIGlmIG5lZWRlZCAob3B0aW9uYWwpCiAgICBjb21iaW5lZF9kZiA8LSByYmluZChjb21iaW5lZF9kZiwgZGYpCiAgfQoKICAjIFJlbW92ZSBkdXBsaWNhdGVzIChpbiBjYXNlIHNhbWUgZ2VuZSBhcHBlYXJzIG11bHRpcGxlIHRpbWVzKQogIGNvbWJpbmVkX2RmIDwtIHVuaXF1ZShjb21iaW5lZF9kZikKCiAgIyBEZXRlcm1pbmUgY29tYmluZWQgcHJlZGljdGVkIG1vZHVsZXMKICBpZiAoaXMuY2hhcmFjdGVyKG1vZHVsZXMpKSB7CiAgICBwcmVkaWN0ZWRfbW9kdWxlcyA8LSBtb2R1bGVzCiAgfSBlbHNlIGlmIChpcy5saXN0KG1vZHVsZXMpKSB7CiAgICBwcmVkaWN0ZWRfbW9kdWxlcyA8LSB1bmlxdWUodW5saXN0KG1vZHVsZXMpKQogIH0gZWxzZSB7CiAgICBzdG9wKHBhc3RlKCJJbnZhbGlkIHByZWRpY3RlZCBtb2R1bGVzIGZvcm1hdCBmb3IgbWV0aG9kIiwgbWV0aG9kKSkKICB9CgogICMgRXZhbHVhdGUgb25jZSBmb3IgdGhlIGNvbWJpbmVkIGdlbmUtbW9kdWxlIG1hcHBpbmcKICByZXMgPC0gZXZhbHVhdGVfbWV0aG9kKAogICAgdHJ1ZV9wb3NpdGl2ZV9nZW5lcyA9IHRydWVfcG9zX2Rpc2Vhc2VkX2dlbmVzLAogICAgcHJlZGljdGVkX3Bvc2l0aXZlX21vZHVsZXMgPSBwcmVkaWN0ZWRfbW9kdWxlcywKICAgIGdlbmVfbW9kdWxlX2RmID0gY29tYmluZWRfZGYKICApCgogIHJlc3VsdHNfbGlzdFtbbWV0aG9kXV0gPC0gZGF0YS5mcmFtZSgKICAgIG1ldGhvZCA9IG1ldGhvZCwKICAgIFRQID0gcmVzJFRQLAogICAgRlAgPSByZXMkRlAsCiAgICBUTiA9IHJlcyRUTiwKICAgIEZOID0gcmVzJEZOLAogICAgcHJlY2lzaW9uID0gcmVzJHByZWNpc2lvbioxMDAsCiAgICByZWNhbGwgPSByZXMkcmVjYWxsKjEwMCwKICAgIHJvdy5uYW1lcyA9IE5VTEwKICApCn0KCiMgQ29tYmluZSBhbGwgcmVzdWx0cyBpbnRvIG9uZSBzdW1tYXJ5IGRhdGEgZnJhbWUKc3VtbWFyeV9kZiA8LSBkby5jYWxsKHJiaW5kLCByZXN1bHRzX2xpc3QpCnJvd25hbWVzKHN1bW1hcnlfZGYpIDwtIHN1bW1hcnlfZGYkbWV0aG9kCnN1bW1hcnlfZGYkbWV0aG9kIDwtIE5VTEwKCiMgVmlldyByZXN1bHRzCnByaW50KHN1bW1hcnlfZGYpCmBgYAo=